TL;DR: Sentence Transformer 모델을 서비스나 API로 배포하려면 FastAPI 또는 Flask를 사용해 REST 엔드포인트를 구성하거나, 대규모 프로덕션 환경에서는 TorchServe를 활용하면 효율적이고 확장 가능한 임베딩 서비스를 구축할 수 있습니다.
왜 Sentence Transformer를 API로 배포해야 할까요?
Sentence Transformer는 텍스트를 고밀도 벡터(임베딩)로 변환하는 강력한 모델입니다. 시맨틱 검색, 문서 유사도 측정, 추천 시스템 등 다양한 NLP 애플리케이션에서 핵심 역할을 합니다. 하지만 모델을 단순히 로컬에서 실행하는 것과 실제 서비스로 배포하는 것은 완전히 다른 이야기입니다.
API로 배포하면 여러 애플리케이션이 동시에 모델을 호출할 수 있고, 팀 전체가 공통 임베딩 서비스를 공유할 수 있습니다. 또한 모델 업데이트 시 클라이언트 코드를 변경하지 않아도 되는 유연성을 제공합니다. 이 가이드에서는 세 가지 주요 방법인 Flask, FastAPI, TorchServe를 단계별로 살펴보겠습니다.
배포 전 준비사항
필수 패키지 설치
배포를 시작하기 전에 기본 환경을 설정해야 합니다. Python 3.8 이상을 권장하며, 가상 환경을 사용하는 것이 좋습니다.
• sentence-transformers: 핵심 모델 라이브러리
• torch: PyTorch 백엔드
• fastapi 또는 flask: 웹 프레임워크
• uvicorn: FastAPI용 ASGI 서버
• numpy: 벡터 처리
모델은 애플리케이션 시작 시 한 번만 로드하여 메모리에 유지하는 것이 성능 최적화의 핵심입니다. 매 요청마다 모델을 로드하면 응답 시간이 크게 늘어납니다.
FastAPI로 Sentence Transformer API 구축하기
FastAPI는 현재 가장 인기 있는 Python API 프레임워크 중 하나로, 비동기 처리와 자동 문서 생성 기능이 뛰어납니다. Sentence Transformer와 결합하면 빠르고 안정적인 임베딩 API를 만들 수 있습니다.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
from typing import List
import numpy as np
import uvicorn
app = FastAPI(title="Sentence Transformer API", version="1.0.0")
# 앱 시작 시 모델 한 번만 로드
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
class EmbeddingRequest(BaseModel):
texts: List[str]
batch_size: int = 32
class EmbeddingResponse(BaseModel):
embeddings: List[List[float]]
model_name: str
dimension: int
@app.get("/health")
async def health_check():
return {"status": "healthy", "model_loaded": True}
@app.post("/embed", response_model=EmbeddingResponse)
async def get_embeddings(request: EmbeddingRequest):
if not request.texts:
raise HTTPException(status_code=400, detail="텍스트 목록이 비어 있습니다.")
if len(request.texts) > 500:
raise HTTPException(status_code=400, detail="한 번에 최대 500개의 텍스트만 처리 가능합니다.")
embeddings = model.encode(
request.texts,
batch_size=request.batch_size,
convert_to_numpy=True,
normalize_embeddings=True
)
return EmbeddingResponse(
embeddings=embeddings.tolist(),
model_name="all-MiniLM-L6-v2",
dimension=embeddings.shape[1]
)
@app.post("/similarity")
async def compute_similarity(text1: str, text2: str):
emb1 = model.encode([text1], normalize_embeddings=True)
emb2 = model.encode([text2], normalize_embeddings=True)
similarity = float(np.dot(emb1[0], emb2[0]))
return {"similarity": similarity, "text1": text1, "text2": text2}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)
위 코드에서 주목할 점은 normalize_embeddings=True 옵션입니다. 이를 설정하면 코사인 유사도 계산 시 단순 내적만으로 결과를 얻을 수 있어 성능이 향상됩니다. 또한 헬스체크 엔드포인트를 추가해 쿠버네티스나 로드밸런서가 서비스 상태를 확인할 수 있도록 했습니다.
Flask로 간단한 임베딩 서비스 만들기
Flask는 더 가벼운 프레임워크로, 소규모 프로젝트나 빠른 프로토타이핑에 적합합니다. FastAPI보다 설정이 간단하지만, 비동기 처리 기능은 제한적입니다.
Flask 서버를 구성할 때는 gunicorn과 함께 사용하는 것을 권장합니다. 단, Sentence Transformer 모델은 멀티프로세싱 환경에서 메모리를 많이 사용하므로, 워커 수를 신중하게 설정해야 합니다. 일반적으로 CPU 코어 수의 2배보다 적게 설정하는 것이 안전합니다.
Flask 배포 시 주요 고려사항은 다음과 같습니다:
• 모델을 전역 변수로 선언하여 워커 간 공유
• 요청 유효성 검사를 위한 JSON 스키마 적용
• 에러 핸들러 등록으로 일관된 오류 응답 제공
• 로깅 설정으로 운영 중 문제 추적
TorchServe로 프로덕션 수준 배포하기
TorchServe란?
TorchServe는 PyTorch 팀이 공식 지원하는 모델 서빙 프레임워크입니다. 모델 버전 관리, A/B 테스트, 동적 배치 처리, 메트릭 수집 등 엔터프라이즈 수준의 기능을 제공합니다. 대규모 트래픽을 처리해야 하는 환경에서는 TorchServe가 최적의 선택입니다.
TorchServe 설정 단계
1. 모델 아카이브 생성: torch-model-archiver를 사용해 .mar 파일 생성
2. 핸들러 작성: BaseHandler를 상속받아 전처리/후처리 로직 구현
3. 서버 시작: torchserve 명령으로 서비스 시작
4. 모델 등록: Management API를 통해 모델 동적 등록
TorchServe의 가장 큰 장점은 동적 배치(Dynamic Batching) 기능입니다. 여러 클라이언트의 요청을 자동으로 묶어서 처리하므로 GPU 활용률이 크게 향상됩니다. 설정 파일에서 batch_size와 max_batch_delay를 조정해 처리량과 지연 시간의 균형을 맞출 수 있습니다.
성능 최적화 및 실전 팁
GPU 가속 활용
CUDA가 설치된 환경에서는 모델을 GPU로 이동시켜 처리 속도를 크게 높일 수 있습니다. SentenceTransformer("model-name", device="cuda")와 같이 device 파라미터를 지정하면 됩니다. GPU 메모리가 제한된 경우 배치 크기를 줄여 OOM 오류를 방지하세요.
모델 최적화 기법
• ONNX 변환: 모델을 ONNX 형식으로 변환하면 CPU 환경에서도 2-3배 빠른 추론이 가능합니다
• 양자화(Quantization): INT8 양자화를 적용하면 모델 크기를 줄이고 추론 속도를 높일 수 있습니다
• 캐싱: Redis나 메모리 캐시를 활용해 동일한 텍스트의 반복 임베딩 계산을 방지하세요
• 비동기 처리: FastAPI의 백그라운드 태스크를 활용해 대용량 배치 처리를 비동기로 수행하세요
컨테이너화 및 클라우드 배포
Docker를 사용해 Sentence Transformer API를 컨테이너화하면 어떤 환경에서도 일관된 배포가 가능합니다. Kubernetes와 결합하면 자동 스케일링과 무중단 배포를 구현할 수 있습니다. 클라우드 환경에서는 AWS SageMaker, Google Cloud Run, Azure Container Instances 등을 활용할 수 있습니다.
코드 없이 AI 모델을 빠르게 테스트하고 배포하고 싶다면 Anakin.ai를 활용해 보세요. Anakin.ai는 복잡한 인프라 설정 없이도 AI 워크플로우를 구축하고 API로 노출할 수 있는 플랫폼으로, 개발자와 비개발자 모두에게 적합한 솔루션을 제공합니다.
FAQ
Q: FastAPI와 Flask 중 어떤 것을 선택해야 할까요?
프로덕션 환경이라면 FastAPI를 강력히 권장합니다. FastAPI는 비동기 처리, 자동 API 문서 생성(Swagger UI), Pydantic을 통한 데이터 유효성 검사 등 현대적인 기능을 기본으로 제공합니다. Flask는 간단한 프로토타입이나 레거시 시스템과의 통합에 적합합니다. 성능 벤치마크에서도 FastAPI가 Flask보다 일반적으로 20-30% 더 빠른 처리량을 보입니다.
Q: 여러 Sentence Transformer 모델을 하나의 API에서 제공할 수 있나요?
네, 가능합니다. 딕셔너리를 사용해 여러 모델을 메모리에 로드한 후, 요청 파라미터로 모델을 선택할 수 있습니다. 예를 들어 {"model": "multilingual", "texts": [...]}와 같이 요청을 구성하면 됩니다. 단, 모델마다 수백 MB에서 수 GB의 메모리를 사용하므로, 서버 메모리 용량을 고려해 로드할 모델 수를 결정하세요. 메모리가 제한된 환경에서는 LRU 캐시를 적용해 자주 사용되는 모델만 메모리에 유지하는 전략이 효과적입니다.
Q: API 응답 속도가 너무 느릴 때 어떻게 개선할 수 있나요?
응답 속도 개선을 위한 체크리스트를 확인하세요. 첫째, 모델이 GPU에서 실행되고 있는지 확인하세요. 둘째, 배치 처리를 활용해 여러 텍스트를 한 번에 처리하세요. 셋째, 자주 요청되는 텍스트의 임베딩을 Redis에 캐싱하세요. 넷째, ONNX Runtime으로 모델을 변환해 CPU 추론 속도를 높이세요. 다섯째, 더 작은 모델(예: all-MiniLM-L6-v2)로 교체해 속도와 품질의 균형을 맞추는 것도 좋은 방법입니다. 대부분의 경우 이러한 최적화를 조합하면 응답 시간을 50% 이상 단축할 수 있습니다.