AWS 기술 블로그

AWS Lambda를 이용한 XGBoost 머신러닝(ML) 추론하기

XGBoost(eXtreme Gradient Boosting)는 분류(Classification)와 회귀(Regression)문제에 모두 적용할 수 있는 빠르고 효과적인 머신러닝 알고리즘입니다. 또한, 대표적­­인 AWS의 서버리스(Serverless) 서비스인 AWS Lambda는 운영에 대한 부담을 줄여주고 사용한 만큼만 지불(Pay As You Go)하면 되기 때문에 다양한 어플리케이션에서 유용하게 활용되고 있습니다. 특히, 2020년 12월부터 Lambda가 컨테이너 이미지를 지원함으로써, Lambda를 머신러닝(Machine Learning) 추론을 배포하는 용도로 사용할 수 있게 되었습니다.

이번 게시글에서는 AWS Lambda에서 XGBoost 알고리즘을 이용한 머신러닝 추론(Inference)을 수행하고, 외부에서 접속할 수 있는 추론 API를 생성하기 위해 AWS CDK V2를 이용합니다. 또한, AWS IAM 인증을 이용하여 클라이언트에서 안전하게 추론 API를 호출할 수 있도록 합니다.

아키텍처 개요

전체적인 아키텍처는 아래와 같습니다. 데이터 사이언티스트가 머신러닝 학습(Training)을 통해 개발한 XGBoost 모델은 Docker 이미지 형태로 AWS Lambda에 탑재되고, Lambda Function URL를 통해 추론 API를 제공합니다. 디바이스와 같은 클라이언트는 AWS IAM을 통해 안전하게 추론용 RESTful API를 요청함으로써 머신러닝 추론을 수행할 수 있습니다. 모델이 변경될 경우에 AWS CDK를 통해 Docker 이미지로 빌드하고, Amazon ECR에 업로드된 이미지를 AWS Lambda에 배포합니다.

XGBoost 머신러닝 알고리즘 준비하기

여기서는 Wine Quality을 예측하는 문제를 해결하기 위해 XGBoost에 기반한 머신러닝 알고리즘을 구현합니다.

데이터의 준비

이번 게시글에서 사용할 Wine Quality Data Set은 아래와 같습니다.

Jupyter Nootbook 파일(xgboost-wine-quality-EDA.ipynb)에서 피처 엔지니어링(Feature Engineering)을 수행하여 결과파일을 wine_concat.csv로 만듭니다.

XGBoost 알고리즘 최적화

XGBoost 회귀(Regression) 모델을 이용하여, Bayesian 방식으로 하이퍼 파라미터를 최적화 합니다. Jupyter Notebook 파일(xgboost-wine-quality.ipynb)에서 상세한 과정을 설명하고 있습니다.

학습(Training)

Jupyter Notebook으로 작성된 코드를 xgboost-wine-quality.py와 같이 Python 코드로 변환하여, 머신러닝 학습(Training)을 수행합니다. 학습의 결과로 얻어진 모델은 xgboost_wine_quality.json와 같이 저장되고, 모델 테스트를 위한 테스트 데이터셋(sample.json)도 함께 생성합니다. 상세한 과정은 아래와 같습니다.

  1. 확장자가 ipynb인 Jupyter Notebook 파일을 아래 명령어를 이용하여 python 파일로 변환합니다. 아래 명령은 xgboost-wine-quality.ipynb 파일을 step0-xgboost-wine-quality.py 로 변환하는 예시입니다.
    jupyter nbconvert xgboost-wine-quality.ipynb --to script --output step0-xgboost-wine-quality
  2. Jupyter Notebook에서 데이터 구조를 이해하기 위해 사용했던 코드들은 본격적인 학습에서는 사용되지 않습니다. 따라서, step1-xgboost-wine-quality.py와 같이 불필요한 코드를 정리합니다.
  3. 함수 형태로 refactoring을 하면, 코드가 읽기 쉬워지고 유지 관리가 용이해집니다. step2-xgboost-wine-quality.py와 같이 함수 형태로 refactoring을 수행합니다. 이때, main은 진입점(entry point)이므로 실행중인지 여부를 확인하기 위해 아래처럼 사용합니다.
    if __name__ == '__main__':
       main()
  4. 이제 Jupyter Notebook 파일이 python 코드로 변환되었음으로 아래와 같이 학습을 수행합니다.
    python3 xgboost-wine-quality.py

추론 (Inference)

XGBoost Regression을 이용해 생성된 모델로 AWS Lambda에서 추론(inference)을 수행하기 위해서는 Lambda의 입력인 event의 형태로 데이터를 받아서 처리할 수 있어야 합니다. inference.py는 event를 입력으로 받아서, 추론에 적합한 형태로 변환한 후에 추론을 수행하고 결과를 반환합니다.

추론이 잘 동작하는지 확인하기 위해 inference-test.py에서 samples.json을 로드하여 inference.py의 handler()를 호출합니다. 추론 동작은 아래와 같이 확인 할 수 있습니다.

python3 inference-test.py

AWS CDK로 Lambda에서 추론(Inference) 시스템 구성

IaC(Infrastructure as Code) 도구인 AWS CDK로 Docker Image를 빌드하고 Amazon ECR에 업로드한 후 이미지를 Lambda에서 활용할 수 있습니다. 또한, 추론용 API를 외부에서 접속할 수 있도록 Lambda Function URL을 활용하면 쉽고 편리하게 외부에서 접속할 수 있습니다.

Docker 이미지 준비

이전 단계에서 학습된 XGBoost Regression모델인 xgboost_wine_quality.json을 이용하여 inference.py로 추론을 수행합니다. AWS Lambda에서 XGBoost 등의 머신러닝 라이브러리를 이용하기 위하여 Docker 이미지를 사용하여야 합니다. 아래와 같이 Dockerfile을 준비합니다.

FROM amazon/aws-lambda-python:3.8
RUN /var/lang/bin/python3.8 -m pip install --upgrade pip
RUN /var/lang/bin/python3.8 -m pip install joblib
RUN /var/lang/bin/python3.8 -m pip install scikit-learn

WORKDIR /var/task/lambda-with-ML-container
COPY inference.py /var/task/
COPY . .

RUN pip install -r requirements.txt
CMD ["inference.handler"]

Dockerfile에서는 AWS Lambda와 Python 3.8 을 사용하기 위하여 AWS에서 제공하는 이미지를 활용합니다. 먼저 pip, joblib, scikit-learn 등 필수 라이브러리를 설치하고, directory를 지정하고 필요한 파일들을 복사합니다. 또한 requirements.txt에 따라 필요한 라이브러리를 버전에 맞추어 설치합니다. 여기서는 inference.py의 handler()를 이용해 추론(inference)를 수행합니다. 이때 사용하는 모델은 xgboost_wine_quality.json입니다.

기본 CDK 설정

“cdk-ml-lambda”와 같은 폴더를 생성하여, 아래와 같이 CDK 초기화를 수행합니다.

mkdir cdk-ml-lambda && cd cdk-ml-lambda
cdk init app --language typescript

아래와 같이 bootstrap을 수행합니다. AWS 계정 당 한번만 수행하면 됩니다.

cdk bootstrap aws://123456789012/ap-northeast-2

여기서 “123456789012”는 AWS account number입니다. 이 값은 AWS Console에서 확인할 수 있고, 아래와 같이 AWS CLI 명령어로 확인할 수도 있습니다.

aws sts get-caller-identity --query Account --output text

Lambda 생성

위의 “cdk init” 과정을 통해, cdk-ml-lambda-stack.ts가 생성되었습니다. 이미지를 로드하여 Lambda를 구성하기 위해 아래와 같이 lambda.DockerImageFunction()를 이용합니다. 여기서는 Lambda의 이름을 편의상 “ML-XGBoost”로 지정하였는데, 중복되지 않도록 설정하여야 합니다. 또한 DockerImageCode.fromImageAsset을 설정하면, 해당 소스 디렉토리에서 Docker Image를 자동으로 빌드하고, Amazon ECR에 업로드 한 후 Lambda에서 로드할 수 있으므로 빌드 및 배포를 편리하게 수행할 수 있습니다.

const mlLambda = new lambda.DockerImageFunction(this, "ml-lambda", {
     description: 'lambda function url for ML',
     functionName: 'ML-XGBoost',
     code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src')),
     timeout: cdk.Duration.seconds(30),
   });

Version과 Alias 설정

아래와 같이 version과 alias를 지정할 수 있습니다. “cdk deploy”를 다시 할 때마다 자동으로 version은 1씩 증가합니다.

   const version = mlLambda.currentVersion;
   const alias = new lambda.Alias(this, 'LambdaAlias', {
     aliasName: 'Dev',
     version,
   });

결과적으로 아래와 같이 Lambda의 Alias가 설정되었음을 알 수 있습니다.

Lambda Function URL 생성

외부에서 Lambda에 직접 접속할 수 있도록 Lambda Function URL을 이용하여 Endpoint로 지정합니다. 이때 Role을 아래와 같이 설정하고 Role 이름도 확인합니다. Role 이름은 client에서 사용합니다.

   // Lambda function url for simple endpoint
   const fnUrl = mlLambda.addFunctionUrl({
     authType: lambda.FunctionUrlAuthType.AWS_IAM // NONE,
   });

   // define the role of function url
   const fnUrlRole = new iam.Role(this, 'fnUrlRole', {
     assumedBy: new iam.AccountPrincipal(cdk.Stack.of(this).account),
     description: 'Role for lambda function url',
   });

   // apply the defined role
   fnUrl.grantInvokeUrl(fnUrlRole);

   // check the arn of funtion url role
   new cdk.CfnOutput(this, 'fnUrlRoleArn', {
     value: fnUrlRole.roleArn,
     description: 'The arn of funtion url role',
   });

외부에서 접속시에 사용할 Endpoint 주소를 아래와 같이 확인합니다.

   new cdk.CfnOutput(this, 'EndpointUrl', {
     value: fnUrl.url,
     description: 'The endpoint of Lambda Function URL',
   });

인프라 설치

인프라 설치는 CDK 폴더에서 아래 명령어를 이용해 수행합니다.

cdk deploy

이때 출력된 결과에서 Role ARN과 Endpoint Address를 아래처럼 알 수 있습니다. 아래 예시에서는 client가 접속하는 주소는 “samplet4zi2bqfx6k42fo26agi0kcght.lambda-url.ap-northeast-2.on.aws” 가 됩니다.

Outputs:
CdkMlLambdaStack.fnUrlRoleArn = arn:aws:iam::123456789012:role/CdkMlLambdaStack-fnUrlRoleF3FB2EB9-1H0ZW8VRW5AM3
CdkMlLambdaStack.EndpointUrl = https://samplet4zi2bqfx6k42fo26agi0kcght.lambda-url.ap-northeast-2.on.aws/

클라이언트에서 추론 요청

생성된 추론 인프라의 동작을 Node.JS로 만든 클라이언트를 통해 확인할 수 있습니다. 이때 필요한 Endpoint 주소는 CDK로 인프라 생성 할 때 확인할 수 있지만, Lambda Console 메뉴의 “Function URL”에서도 확인할 수 있습니다. 추론 테스트용 데이터셋은 학습시 생성한 samples.json을 이용합니다.

Lambda Function URL의 인증방식으로 AWS IAM을 사용하므로, client.js와 같이 AWS SDK에서 제공하는 Temporary Security Credential을 이용하여 안전하게 추론 API를 사용할 수 있습니다. 실행 전에 client.js 파일 내에 있는 Lambda Function URL의 Endpoint와 Role ARN을 아래 예시와 같이 업데이트하여야 합니다.

const domain = 'samplet4zi2bqfx6k42fo26agi0kcght.lambda-url.ap-northeast-2.on.aws';
const roleArn = 'arn:aws:iam::1234567890:role/CdkMlLambdaStack-fnUrlRoleF3FB2EB9-1H0ZW8VRW5AM3';

이제 client 폴더로 이동하여, 아래처럼 실행하여 추론을 수행합니다.

cd client && node client.js

도커 이미지 디버깅

Docker 이미지에 설치된 라이브러리의 버전정보 등이 학습 환경과 다른 경우에는 정상적으로 동작하지 않을 수 있습니다. 이를 인프라 설치 후에 로그를 통해 확인하면 반복적으로 인프라를 재설치하여야 하므로 번거로울 수 있습니다. 아래와 같이 docker 이미지에서 동작을 직접 확인하면 반복적인 디버깅없이 편리하게 동작을 확인 할 수 있습니다. 먼저, 소스 폴더로 이동하여 이미지를 빌드합니다.

cd src
docker build -t inference:v1 .

Docker를 실행하고, 컨테이너 정보를 확인합니다

docker run -d -p 8080:8080 inference:v1
docker ps

아래와 같이 Container ID를 확인 할 수 있습니다.

CONTAINER ID   IMAGE         COMMAND                 CREATED         STATUS         PORTS                   NAMES
41e297948511   inference:v1   "/lambda-entrypoint.…"   6 seconds ago   Up 4 seconds   0.0.0.0:8080->8080/tcp   stupefied_carson

Bash shell로 컨테이너에 접속합니다.

docker exec -it 41e297948511 /bin/bash

아래처럼 inference-test.py를 실행하여 추론 동작을 확인할 수 있습니다. 정상적인 경우에 아래와 같이 사용된 라이브러리를 확인하고, 추론 결과를 보여줍니다.

python3 inference-test.py

추론 결과는 아래 예시와 같이 확인할 수 있습니다.

np version: 1.23.4
pandas version: 1.5.1
xgb version: 1.6.2
event: {'body': '[{"fixed acidity":6.6,"volatile acidity":0.24,"citric acid":0.28,"residual sugar":1.8,"chlorides":0.028,"free sulfur dioxide":39,"total sulfur dioxide":132,"density":0.99182,"pH":3.34,"sulphates":0.46,"alcohol":11.4,"color_red":0,"color_white":1},{"fixed acidity":8.7,"volatile acidity":0.78,"citric acid":0.51,"residual sugar":1.7,"chlorides":0.415,"free sulfur dioxide":12,"total sulfur dioxide":66,"density":0.99623,"pH":3.0,"sulphates":1.17,"alcohol":9.2,"color_red":1,"color_white":0}]', 'isBase64Encoded': False}
values:     fixed acidity volatile acidity citric acid residual sugar ... sulphates alcohol color_red color_white
0           6.6             0.24         0.28             1.8 ...       0.46     11.4         0          1
1           8.7             0.78         0.51             1.7 ...       1.17     9.2         1           0
result: [6.573914 4.869721]

Elapsed time: 0.02s

리소스 정리하기

AWS CDK로 생성된 인프라는 아래와 같이 삭제할 수 있습니다.

cdk destroy

결론

지금까지 XGBoost 머신러닝 알고리즘을 활용하여 학습(Training) 및 추론(Inference)을 진행하고, AWS CDK를 이용하여 추론 API를 제공하는 AWS Lambda Function URL를 생성해 보았습니다. 이제 여러분들은 서버리스 추론 API를 통해 빠르고 비용효율적으로 머신러닝 추론을 비즈니스에 활용할 수 있습니다.

머신러닝은 전문적인 연구의 단계를 벗어나서, 일반적인 엔지니어도 쉽게 사용할 수 있는 응용의 단계에 있습니다. AWS Lambda는 서버리스 환경에서 머신러닝 추론 API를 제공할 수 있는 매우 유용한 방법으로서, IoT 디바이스를 포함한 수없이 많은 비즈니스에서 유용하게 활용될 것으로 기대됩니다.

Kyoung-Su Park

Kyoung-Su Park

박경수 솔루션즈 아키텍트는 다양한 워크로드에 대한 개발 경험을 바탕으로 고객이 최적의 솔루션을 선택하여 비즈니스 성과를 달성할 수 있도록 고객과 함께 효율적인 아키텍처를 구성하는 역할을 수행하고 있습니다. 현재 AWS의 Machine Learning, IoT, Analytics TFC에서 활동하고 있습니다.