실시간 추론을 위한 모델 배포(REST API)¶
참고
snowflake-ml-python 버전 1.25.0부터 일반 공급으로 제공됩니다.
대기 시간이 짧아야 하는 대화형 워크플로에는 실시간 추론을 사용합니다. 모델을 :doc:`Snowflake 모델 레지스트리 </developer-guide/snowflake-ml/model-registry/overview>`에서 전용 HTTP 엔드포인트를 사용하는 관리형 서비스로 배포할 수 있습니다. 관리형 서비스는 크기를 자동으로 조정하며 Snowflake 에코시스템 내에 완전히 통합되어 포괄적인 가시성을 제공합니다.
다음과 같은 경우 워크플로에 온라인 추론을 사용합니다.
애플리케이션에서 즉각적으로 응답하기 위해 대기 시간이 짧아야 합니다.
모델이 사용자에게 표시되는 웹 또는 모바일 애플리케이션의 백엔드 역할을 합니다.
모델에 대한 입력이 요청의 HTTP 페이로드 내에 적합할 수 있습니다.
서비스가 변동하는 요청 볼륨을 처리하기 위해 자동으로 수평 확장되어야 합니다.
작동 방법¶
Snowflake는 모델을 Snowpark Container Services(SPCS) 내의 HTTP 서버로 호스팅하여 배포 파이프라인을 간소화합니다. 이 아키텍처를 통해 다음을 수행할 수 있습니다.
추상 복잡성: Docker 이미지 또는 Kubernetes 클러스터를 관리하지 않고도 정교한 모델을 배포할 수 있습니다.
크기 조정 성능: 고성능 요구 사항을 충족하기 위해 분산 GPU 클러스터에서 대규모 모델을 실행합니다.
신뢰성 보장: 원활한 모델 업그레이드를 위해 기본 제공되는 가시성, 트래픽 분할, 섀도우/카나리 배포를 활용합니다.
전제 조건¶
시작하기 전에 다음이 있는지 확인하십시오.
필수 권한¶
모델 서비스는 Snowpark Container Services 에서 실행됩니다. Model Serving을 사용하려면 다음 권한이 필요합니다.
서비스가 실행되는 컴퓨팅 풀에 대한 USAGE 또는 OWNERSHIP 권한. 또는 기본 시스템 컴퓨팅 풀을 사용할 수 있습니다.
공용 엔드포인트를 생성할 수 있는 계정에 대한 BIND SERVICE ENDPOINT 권한.
모델에 대한 OWNER 또는 READ 권한
제한 사항¶
Snowpark Container Services에서 제공하는 온라인 모델에는 다음과 같은 제한이 적용됩니다.
테이블 함수는 지원되지 않습니다. 모델에 Snowflake에 배포할 테이블 함수가 있어야 합니다.
Snowpark ML 모델링 클래스를 사용하여 개발된 모델은 GPU가 있는 환경에는 배포할 수 없습니다. 해결 방법으로 네이티브 모델을 추출하여 배포할 수 있습니다. 자세한 내용은 온라인 추론을 위한 모델 배포 섹션을 참조하십시오.
온라인 추론을 위한 모델 배포¶
Snowflake ML은 모델 버전 오브젝트를 사용하여 추론 요청을 처리하는 모델 서비스를 생성합니다. 모델 버전 오브젝트를 생성하려면 새 모델 버전을 기록하거나 기존 모델 버전에 대한 참조를 획득하면 됩니다. 모델 버전 오브젝트를 가져온 후 다음 Python 코드를 사용하여 모델 서비스를 생성하고 해당 서비스를 SPCS에 배포할 수 있습니다.
# reg is a snowflake.ml.registry.Registry object
example_mv_object = reg.get_model("mymodel_name").version("version_name") # a snowflake.ml.model.ModelVersion object
example_mv_object.create_service(service_name="myservice",
service_compute_pool="my_compute_pool",
ingress_enabled=True,
gpu_requests=None)
:code:`create_service`에는 다음 인자가 필요합니다.
service_name: 생성 중인 서비스의 이름입니다. 이 이름은 Snowflake 계정 내에서 고유해야 합니다.
service_compute_pool: 모델을 실행하는 데 사용하는 컴퓨팅 풀의 이름입니다. 컴퓨팅 풀은 기존에 있어야 합니다. 모델이 시스템 컴퓨팅 풀에 적합한 경우
SYSTEM_COMPUTE_POOL_GPU또는 :code:`SYSTEM_COMPUTE_POOL_CPU`도 사용할 수 있습니다.ingress_enabled: Snowflake 외부에서 온라인 추론을 호출하려면 이 값이 True여야 합니다.
gpu_requests: GPUs의 개수를 지정하는 문자열입니다. CPU 또는 여러 GPUs에서 실행할 수 있는 모델의 경우 이 인자에 따라 모델이 CPU에서 또는 GPUs에서 실행될지가 결정됩니다. 모델이 CPU에서만 실행할 수 있는 알려진 유형(예: scikit-learn 모델)인 경우, GPUs가 요청되면 이미지 빌드가 실패합니다. 새 모델을 배포하는 경우 CPU 기반 모델의 서비스를 생성하는 데 최대 10분, GPU 기반 모델의 서비스를 생성하는 데 20분이 소요될 수 있습니다. 컴퓨팅 풀이 유휴 상태이거나 크기를 조정해야 하는 경우 서비스를 생성하는 데 시간이 더 오래 걸릴 수 있습니다.
이전 예제에서는 필수 인자와 가장 일반적으로 사용되는 인자만 보여줍니다. 전체 인자 목록은 ModelVersion API 참조 섹션을 참조하세요.
기본 서비스 구성¶
배포한 모델을 실행하는 서버는 대부분의 사용 사례에 적합한 기본값을 사용합니다.
워커 스레드 수: CPU 기반 모델의 경우 서버에서 사용하는 프로세스의 수는 CPUs 수의 2배에 하나를 더한 수입니다. GPU 기반 모델은 하나의 워커 프로세스를 사용합니다. create_service 호출에서 num_workers 인자를 사용하여 이를 재정의할 수 있습니다. 모델이 메모리에 들어갈 수 있는 가장 작은 GPU 노드를 지정하는 것을 **권장**합니다. 인스턴스 수를 늘려 확장합니다. 예를 들어, 모델이 GPU_NV_S(Azure에서는 GPU_NV_SM) 인스턴스 유형에 맞는 경우 gpu_requests=1을 사용하고 max_instances를 늘려 확장합니다. 그러나 사용 가능한 가장 작은 노드에 4개의 GPUs가 있는데 2개만 필요한 경우 :code:`num_workers=2`(즉, 사용 가능한 gpu/모델에 필요한 gpu)를 사용합니다.
스레드 안전: 일부 모델은 스레드에 안전하지 않습니다. 따라서 서비스는 각 워커 프로세스에 대해 별도의 모델 복사본을 로드합니다. 이로 인해 대규모 모델의 경우 리소스 고갈이 발생할 수 있습니다.
노드 사용률: 기본적으로 하나의 추론 서버 인스턴스는 실행 중인 노드의 모든 CPU 및 메모리를 요청하여 전체 노드에 요청합니다. 인스턴스당 리소스 할당을 사용자 지정하려면 cpu_requests, memory_requests, gpu_requests와 같은 인자를 사용합니다.
엔드포인트: 추론 엔드포인트의 이름은 추론이며, 포트 5000을 사용합니다. 이들은 사용자 지정할 수 없습니다. 최적의 리소스 활용을 위해 모델을 메모리에 맞출 수 있는 가장 작은 GPU 노드를 지정합니다. 워크로드에 맞게 확장할 인스턴스 수를 늘립니다. 예를 들어, 모델이 GPU_NV_S(Azure에서는 GPU_NV_SM) 인스턴스 유형에 맞는 경우 gpu_requests=1을 사용하고 max_instances를 늘려 확장합니다.
컨테이너 이미지 빌드 동작¶
Snowflake conda 채널은 웨어하우스에서만 사용할 수 있으며 웨어하우스 종속성에 대한 유일한 소스입니다. 기본적으로 SPCS 모델에 대한 conda 종속성은 conda-forge에서 종속성을 가져옵니다.
기본적으로 Snowflake Model Serving은 모델을 실행하는 데 사용되는 것과 동일한 컴퓨팅 풀을 사용하여 컨테이너 이미지를 빌드합니다. 컴퓨팅 풀로 인해 이미지 빌드 프로세스에 대해 과부하가 발생할 수 있습니다(예: 컨테이너 이미지 빌드에 GPUs가 사용되지 않음). 대부분의 경우 이는 컴퓨팅 비용에 큰 영향을 미치지 않습니다. 그러나 우려된다면 image_build_compute_pool 인자로 이미지 빌드에 덜 강력한 컴퓨팅 풀을 지정할 수 있습니다.
create_service()를 여러 번 호출해도 호출할 때마다 빌드가 트리거되는 것은 아닙니다.
그러나 Snowflake가 종속 패키지의 취약점에 대한 수정 사항을 포함하여 추론 서비스를 업데이트한 경우 컨테이너 이미지가 다시 빌드될 수 있습니다. 이러한 상황이 발생하면 create_service가 이미지의 다시 빌드를 자동으로 트리거합니다.
사용자 인터페이스¶
배포된 모델은 Model Registry Snowsight UI 에서 관리할 수 있습니다. 자세한 내용은 모델 추론 서비스 섹션을 참조하십시오.
배포된 모델 호출하기¶
HTTP 엔드포인트¶
모든 서비스에는 내부 DNS 이름이 있습니다. ingress_enabled로 서비스를 배포하면 Snowflake 외부에서 사용할 수 있는 공용 HTTP 엔드포인트도 생성됩니다. 두 엔드포인트 중 하나를 사용하여 서비스를 호출할 수 있습니다.
SHOW ENDPOINTS 명령을 사용하여 수신이 활성화된 서비스의 공용 HTTP 엔드포인트를 찾을 수 있습니다. 출력에는 unique-service-id-account-id.snowflakecomputing.app 형식의 항목이 있는 ingress_url 열이 포함됩니다. 이는 서비스에 대해 공개적으로 사용 가능한 HTTP 엔드포인트입니다. 비공개 링크 사용자의 경우 ingress_url 대신 privatelink_ingress_url을 사용합니다.
Snowflake에서 내부 DNS 이름을 사용하려면 DESCRIBE SERVICE 명령을 사용합니다. 이 명령 출력의 dns_name 열에는 서비스의 내부 DNS 이름이 포함됩니다. 서비스의 포트를 찾으려면 SHOW ENDPOINTS IN SERVICE 명령을 사용합니다. 포트 또는 port_range 열에는 서비스에서 사용하는 포트가 포함됩니다. URL http://dns_name:*port*를 통해 서비스에 내부 호출을 수행할 수 있습니다.
모델의 특정 메서드를 호출하려면 메서드 이름을 URL의 경로로 사용합니다(예: https://unique-service-id-account-id.snowflakecomputing.app/method-name 또는 http://dns_name:port/<method-name>). URL에서 메서드 이름의 밑줄(_)은 URL에서 대시(-)로 대체됩니다. 예를 들어, 서비스 이름 predict_prob는 URL에서 predict-proba로 변경됩니다.
작업을 단순화하기 위해 Python에서 ModelVersion 오브젝트에 대해 list_services() API를 호출할 수 있습니다.
# mv: snowflake.ml.model.ModelVersion
mv.list_services()
공용 엔드포인트(inference_endpoint) 및 내부 엔드포인트(internal_endpoint)를 모두 출력합니다.
인증¶
Snowflake는 여러 인증 프로토콜을 지원합니다. 가장 간단한 방법은 :doc:`프로그래밍 방식 액세스 토큰(PAT) </user-guide/programmatic-access-tokens>`을 사용하는 것이며, 여기서 토큰은 :samp:`Authorization: Snowflake Token=”{your_pat_token}”`으로 요청 헤더에 간단히 전달할 수 있습니다.
참고
잘못된 토큰 또는 서비스에 대한 네트워크 경로 부족과 같은 모든 인증 실패에는 404 오류가 발생합니다. 현재, 인증 오류와 유효하지 않은 URLs를 구분할 방법이 없습니다.
요청 본문(또는 프로토콜 또는 데이터 형식)¶
Snowflake는 REST 요청에 대해 두 가지 유형의 데이터 형식을 지원합니다. 특히 Pandas Dataframe에서 영감을 받았는데, 이는 업계에서 잘 알려져 있고 고객이 Pandas Dataframe을 사용한 간단한 Python 스크립트로 검증할 수 있기 때문입니다.
팁
메서드- URL 매핑: 요청 URL을 구성할 때, 모델의 메서드 이름에 있는 밑줄(_)은 자동으로 대시(-)로 대체됩니다. 예를 들어 모델 메서드가 predict_proba`인 경우 엔드포인트 URL 경로는 :code:/predict-proba`가 됩니다.
해당 형식에 대한 세부 정보는 다음과 같습니다.
:code:`dataframe_split`은 압축된 인덱스/열/데이터 표현입니다.
:code:`pandas_df.to_json(orient=”split”)`을 미러링하는 표현입니다.
:code:`dataframe_records`는 키/값(레코드 지향) 표현입니다.
:code:`df.to_json(orient=”records”)`을 미러링하는 표현입니다.
dataframe_split 형식을 사용하는 것을 **권장**합니다. :code:`dataframe_records`는 각 행에 대해 열 이름을 반복하기 때문에 일반적으로 :code:`dataframe_split`보다 큰 요청 본문을 생성합니다. 이는 대규모 배치 또는 빈번한 호출의 경우 성능에 영향을 줄 수 있습니다.
모델 엔드포인트는 사용하는 입력 형식에 관계없이 **단일 출력 형식**을 계속 반환합니다.
dataframe_split형식(권장)
이는 Pandas “분할” 방향에 의해 생성된 구조와 일치합니다. 요청 본문은 dataframe_split 키 아래에 다음 구조를 래핑합니다.
index: 행 인덱스의 목록입니다.columns: 열 이름의 목록입니다.data: 행 목록으로, 각 행은 열에 맞춰 정렬된 값 목록입니다.
cURL 요청 예제:
curl -X POST "<endpoint_url>" \
-H 'Authorization: Snowflake Token="<pat_token>"' \
-H 'Content-Type: application/json' \
-w "\n\n=== RESULT ===\nHTTP Status: %{http_code}\nTotal Time: %{time_total}s\nConnect Time: %{time_connect}s\nServer Processing: %{time_starttransfer}s\nResponse Size: %{size_download} bytes\nRequest Size: %{size_upload} bytes\n" \
-d '{
"dataframe_split": {
"index": [0, 1],
"columns": ["customer_id", "age", "monthly_spend"],
"data": [
[101, 32, 85.5],
[102, 45, 120.0],
]
}
}'
dataframe_records형식
:code:`dataframe_records`는 Pandas 레코드 방향 에 의해 생성된 구조와 일치합니다.
요청 본문은 dataframe_records 키 아래에 이 목록을 래핑합니다.
cURL 요청 예제:
curl -X POST "<endpoint_url>" \
-H 'Authorization: Snowflake Token="<pat_token>"' \
-H 'Content-Type: application/json' \
-w "\n\n=== RESULT ===\nHTTP Status: %{http_code}\nTotal Time: %{time_total}s\nConnect Time: %{time_connect}s\nServer Processing: %{time_starttransfer}s\nResponse Size: %{size_download} bytes\nRequest Size: %{size_upload} bytes\n" \
-d '{
"dataframe_records": [
{
"customer_id": 101,
"age": 32,
"monthly_spend": 85.5,
},
{
"customer_id": 102,
"age": 45,
"monthly_spend": 120.0,
},
]
}'
매개 변수 전달하기¶
모델의 서명에 ParamSpec </developer-guide/snowflake-ml/model-registry/model-signature>`으로 정의된 매개 변수가 포함된 경우 :code:`dataframe_split 또는 dataframe_records`와 함께 JSON 요청 본문에 최상위 :code:`params 키를 포함하여 매개 변수 값을 전달할 수 있습니다. 재정의하려는 매개 변수만 포함합니다. 지정되지 않은 매개 변수는 서명의 기본값을 사용합니다.
매개 변수가 있는 cURL 요청 예제:
curl -X POST "<endpoint_url>/predict" \
-H 'Authorization: Snowflake Token="<pat_token>"' \
-H 'Content-Type: application/json' \
-d '{
"dataframe_split": {
"index": [0],
"columns": ["input_text"],
"data": [["Hello, world!"]]
},
"params": {"temperature": 0.9, "max_tokens": 512}
}'
params 키는 dataframe_records 형식과 동일한 방식으로 작동합니다.
curl -X POST "<endpoint_url>/predict" \
-H 'Authorization: Snowflake Token="<pat_token>"' \
-H 'Content-Type: application/json' \
-d '{
"dataframe_records": [
{"input_text": "Hello, world!"}
],
"params": {"temperature": 0.9, "max_tokens": 512}
}'
Python 예제¶
dataframe_split형식
Snowflake는 요청을 보내기 전에 **Pandas JSON 직렬화**를 사용하여 페이로드를 생성한 다음 :code:`json.loads`로 역직렬화는 것을 권장합니다. 이를 통해 데이터 타입이 일관되게 처리됩니다.
import json
import pandas as pd
import requests
# Example DataFrame
df = pd.DataFrame(
{
"customer_id": [101, 102],
"age": [32, 45],
"monthly_spend": [85.5, 120.0],
}
)
ENDPOINT_URL = "<your endpoint URL>"
HEADERS = {
"Authorization": f'Snowflake Token="{PAT}"',
"Content-Type": "application/json"
}
# Use Pandas to generate the JSON, then load it back to a Python dict
split_obj = json.loads(df.to_json(orient="split"))
payload = {
"dataframe_split": split_obj
}
response = requests.post(
ENDPOINT_URL,
headers=HEADERS,
json=payload,
timeout=30,
)
result = response.json()
요점:
pd.Dataframe.to_json(예:
df.to_json(orient="split"))을 사용하여 네이티브 json 직렬 변환기에 익숙하지 않은 타임스탬프, 부동 소수점, null, 카테고리 등의 유형을 올바르게 처리할 수 있습니다.:code:`json.loads(…)`는 JSON 문자열을 Python 사전으로 변환하여 페이로드를 올바르게 구성할 수 있도록 합니다.
:code:`requests.post(…, json=payload)`는 HTTP 요청을 위해 사전을 다시 JSON으로 직렬화합니다.
매개 변수를 포함하려면 params 키를 페이로드 사전에 추가합니다.
payload = {
"dataframe_split": split_obj,
"params": {"temperature": 0.9, "max_tokens": 512}
}
dataframe_records형식
:code:`dataframe_split`과 마찬가지로, Pandas JSON 직렬화 및 :code:`json.loads`를 사용합니다.
import json
import pandas as pd
import requests
df = pd.DataFrame(
{
"customer_id": [101, 102],
"age": [32, 45],
"monthly_spend": [85.5, 120.0],
}
)
ENDPOINT_URL = "<your endpoint invoke URL>"
HEADERS = {
"Authorization": "Bearer <your token>",
"Content-Type": "application/json",
}
records_obj = json.loads(df.to_json(orient="records"))
payload = {
"dataframe_records": records_obj
}
response = requests.post(
ENDPOINT_URL,
headers=HEADERS,
json=payload,
timeout=30,
)
response.raise_for_status()
result = response.json()
다음 단계¶
추론 서비스를 최적화하고 관리하기 위한 자세한 가이드를 살펴보세요.
워크플로 예제: XGBoost(CPU), Hugging Face(GPU), PyTorch 모델에 대한 엔드투엔드 코드를 참조하세요.
서비스 관리 및 크기 조정: 자동 크기 조정, 수동 일시 중단, 하드웨어 구성에 대해 알아보세요.
안정적인 엔드포인트 및 API 참조: Snowflake 게이트웨이, 인증 및 데이터 프로토콜(
dataframe_split)에 대해 심층 분석합니다.자동 캡처 추론 로그: 모델 모니터링을 위한 자동화된 로깅을 설정합니다.
문제 해결: 패키지 충돌, OOM 오류 및 빌드 실패에 대한 일반적인 수정 사항입니다.