MLOps

[MLOps] AWS SageMaker를 사용한 ML 모델 서빙 도전기(2) - Serverless Inference

binni 2024. 6. 12. 18:52

1. Serverless Inference란?

모델이 수신하는 추론 요청의 볼륨에 따라 컴퓨팅 용량을 자동으로 프로비저닝하고, 크기를 조정하고, 끕니다.
따라서 유휴 시간을 제외하고 추론 코드를 실행하는 데 필요한 컴퓨팅 시간과 처리된 데이터 양에 대해서만 비용을 지불하면 됩니다.

 

  • 이름 그대로 지속적으로 GPU를 항상 켜두는 것이 아니라, 요청이 들어올 때만 GPU 자원을 사용하게끔 하는 방식입니다.
  • 항상 켜두는 비용보다 Serverless 방식을 적용하면 비용이 적게 나가게 됩니다.
  • 그러면 비용이 적게 나간다는 장점만 있진 않습니다. 제가 직접 경험한 바로는 Inference 속도가 기본 추론 방식보다 늦어지게 됩니다. Inference latency - Inference cost와의 trade off 관계가 발생하게 됩니다.
  • 그래서 저희 서비스에 '실시간성이 필요하지 않은' ML 모델 배포방식으로 적용을 하고 있습니다. 

 

2. Serverless Config

  • memory_size_in_mb : 모델 최대 size를 지정합니다. 선택할 수 있는 옵션들로는 1024MB, 2048MB, 3072MB, 4096MB, 5120MB, 6144MB가 있으며, 6144MB를 넘는 모델은 serverless 추론을 진행할 수 없습니다. Quantization까지 진행하면 가능할 듯 합니다. (출처 : AWS 공식 문서)
  • max_concurrency : 최대 concurrency를 지정합니다. 1부터 200까지 지정할 수 있습니다. (출처 : AWS 공식 문서)
import sagemaker
from sagemaker.serverless import ServerlessInferenceConfig

serverless_config = ServerlessInferenceConfig(
    memory_size_in_mb=2048,
    max_concurrency=150
)

 

 

3. Model 구성

  • 일반적인 SageMaker Inference과 똑같이 지정해주면 됩니다. (자세한 사항은 여기에 나와있습니다.)
  • 인자값 설명
    • model_data : s3에 업로드한 모델 url 정보입니다.
    • role : role = get_execution_role() 을 통해 AWS의 IAM role 정보를 적어줍니다.
    • entry_point : 모델을 배포할 때 SageMaker에 의해 호출되는 Python 스크립트 파일의 이름입니다. 이 파일에는 모델을 로드하고, 요청을 처리하고, 추론을 실행하는 데 필요한 코드가 포함되어 있습니다. 보통 inference.py 로 지정합니다.
    • source_dir : entry_point에 지정된 스크립트 파일과 함께 사용할 추가 코드 파일이 포함된 디렉토리의 경로입니다. 모델을 추론에 필요한 추가 코드나 모듈을 포함할 수 있습니다.
    • framework_version : pytorch framework 버전입니다.
    • py_version : SageMaker 환경에서 사용할 Python 버전입니다. py310은 Python 3.10을 의미합니다.
import time
from sagemaker.pytorch import PyTorchModel

model_path = #s3 bucket 주소
role = #IAM role


model = PyTorchModel(
    model_data = model_path,
    role = role,
    entry_point = 'inference.py',
    source_dir = 'code',
    framework_version = '2.0.1',
    py_version = 'py310'
)

 

 

4. 배포

  • 인자값 설명
    • initial_instance_count : 배포할 때 시작할 인스턴스의 수를 정의합니다.
    • instance_type : 배포할 인스턴스의 type을 정의합니다. 만약 local에서 (현재 노트북 인스턴스에서) 테스트를 해보고 싶다면 “local_gpu"로 변경하시면 됩니다.
    • serializer : 클라이언트에서 SageMaker 엔드포인트로 데이터를 전송할 때 사용할 데이터의 형식을 정의합니다. JSONSerializer()는 Python 객체를 JSON 문자열로 변환하여 서버로 전송합니다.
    • deserializer : SageMaker 엔드포인트에서 클라이언트로 데이터를 반환할 때 사용할 데이터의 형식을 정의합니다.
    • serverless_inference_config : 위에서 정의한 serverless config를 전달해줍니다.
from sagemaker.serializers import JSONSerializer
from sagemaker.deserializers import JSONDeserializer

endpoint_name="contriever-serverless"

predictor = model.deploy(
    endpoint_name = endpoint_name,
    initial_instance_count=1,
    instance_type='ml.g4dn.xlarge',
    serializer=JSONSerializer(),
    serverless_inference_config=serverless_config,
    deserializer=JSONDeserializer()
)

 

  • 마찬가지로 ----! 가 뜨면 배포가 완료되었습니다.

 

5. Serverless Inference와 일반적인 Inference의 추론 시간 차이

  • 앞서 Serverless 추론을 하게되면 GPU를 열었다가 닫았다하는 시간이 소요되어 추론 시간이 지연된다는 언급을 했습니다.
  • 그래서 일반 추론과 얼마나 차이가 나는지 시간을 측정해보았습니다. 
  • 시간 측정 코드는 다음과 같습니다. 
import boto3
import json
import time

endpoint_name = predictor.endpoint_name
client = boto3.client('sagemaker-runtime')
content_type = "application/json"

# API 호출 전 시간 기록
start_time = time.time()

# API 호출
response = client.invoke_endpoint(
    EndpointName=endpoint_name,  
    ContentType=content_type,
    Body=json.dumps(data)
)

# API 호출 후 시간 기록
end_time = time.time()

# 호출 속도 계산
duration = end_time - start_time

# 출력
print(f"API call duration: {duration:.2f} seconds")

# 호출 결과
result = response['Body'].read().decode('utf-8')
result

 

 

1) 일반적인 Inference

  • API call duration을 보았을 때, 1.07초가 소요되었습니다. 

 

2) Serverless Inference

  • serverless를 진행하였을 때, API call duration이 3.63초가 나왔습니다. 약 3배정도 느린 것을 확인할 수 있습니다.
  • 하지만 모든 경우에 이러한 상황이 성립하는 것은 아니니, 참고용으로만 생각해주시면 감사하겠습니다. 

 

  • 따라서 저는 실시간성이 필요하지 않은 곳에 사용하는 것이 좋다는 판단을 하였습니다.
  • 비용 절감이라는 장점이 있다면 성능저하,추론속도지연 이라는 단점이 항상 따라오는 걸까요..? 🥲