임베딩 (인코더 전용 모델) (Embeddings)

임베딩 (인코더 전용 모델) (Embeddings (Encoder-Only Models))

LLM뿐 아니라 BERT 스타일 분류기나 리워드 모델, 텍스트 임베딩 모델 같은 인코더 전용 모델도 서빙해야 하는 경우가 있어요. trtllm-serve는 이런 모델을 OpenAI 호환 POST /v1/embeddings 엔드포인트로 서빙하고, 네이티브 동적 배칭까지 지원해요. 여러 독립적인 동시 요청을 하나의 forward pass로 합쳐 처리량을 높이는데, NVIDIA Triton Inference Server의 동적 배처가 하는 방식과 같습니다. 덕분에 인코더 모델 앞에 별도 Triton Inference Server를 띄울 필요 없이, 기존 OpenAI 스타일 임베딩 클라이언트를 trtllm-serve 주소에 맞추기만 하면 그대로 동작해요.

출처: 공식 문서 - Embeddings (Encoder-Only Models)

빠른 시작 (Quick start)

embeddings 서브커맨드로 임베딩 서버를 띄웁니다.

trtllm-serve embeddings <hf_model_or_path> \
    --max_batch_size 32 \
    --max_queue_delay 0.005 \
    --max_queue_size 2048 \
    --host 0.0.0.0 --port 8000

요청은 OpenAI 호환 클라이언트나 curl로 보내면 돼요.

curl http://localhost:8000/v1/embeddings \
    -H "Content-Type: application/json" \
    -d '{"model": "<model>", "input": ["hello world", "foo bar"]}'

응답은 표준 OpenAI 임베딩 형태예요.

{
  "object": "list",
  "data": [
    {"object": "embedding", "index": 0, "embedding": [...]},
    {"object": "embedding", "index": 1, "embedding": [...]}
  ],
  "model": "<model>",
  "usage": {"prompt_tokens": 8, "total_tokens": 8}
}

요청 필드 (Request fields)

엔드포인트는 표준 OpenAI /v1/embeddings 필드를 받아요.

| Field | Type | Notes | | model | str | Model name. | | input | str | list[str] | | encoding_format | "float" (default) | "base64" | | dimensions | int (optional) | Matryoshka output size. Only supported by Matryoshka-trained text-embedding models; rejected with 400 otherwise. None of the served models are Matryoshka-trained (BERT classifiers / reward models emit label/score tensors; Qwen3-Embedding emits a fixed-width pooled vector), so this is currently always rejected. | | user | str (optional) | Ignored; accepted for compatibility. | | add_special_tokens | bool (default true) | TRT-LLM extension. Encoder models such as BERT generally need their special tokens (e.g. [CLS] / [SEP]) added during tokenization. |

TRT-LLM 전용으로 필수 요청 필드는 없어요. 기존 OpenAI 호환 임베딩 클라이언트를 trtllm-serve URL에 맞추면 그대로 작동합니다.

동적 배칭 (Dynamic batching)

서버 안의 가벼운 배처가 인코더 forward pass 앞에서 동시 요청을 합쳐요. Triton 동적 배처를 닮은 세 가지 손잡이를 제공합니다.

| trtllm-serve embeddings flag | Behavior | Triton equivalent | | --max_queue_delay (seconds) | Hold window: how long an incoming request waits for others to join its batch before dispatch. | max_queue_delay_microseconds | | --max_queue_size | Maximum number of in-flight queued requests. Further requests are rejected with HTTP 429 (backpressure). | default_queue_policy.max_queue_size |

배치는 다음 중 어느 하나라도 발동하면 즉시 디스패치돼요. --max_batch_size에 도달했거나, 다음 요청을 추가하면 엔진의 --max_num_tokens 예산을 넘기거나, --max_queue_delay 홀드 윈도우가 지나가는 경우입니다.

Triton Inference Server 동적 배처에서 마이그레이션하기

현재 Triton inflight_batcher_llm 백엔드와 config.pbtxtdynamic_batching { ... } 블록으로 인코더 모델을 서빙 중이라면, 설정을 바로 매핑하면 돼요.

| Triton config.pbtxt | trtllm-serve embeddings | | dynamic_batching.preferred_batch_size / model max batch | --max_batch_size | | dynamic_batching.default_queue_policy.max_queue_size | --max_queue_size |

Triton에서 조정했던 값들을 출발점으로 삼고, 자신의 지연·처리량 예산에 맞게 조정하면 됩니다.

오류 처리 (Error handling)

| Condition | HTTP status | | Input longer than --max_seq_len | 400 | | Request queue full (--max_queue_size reached) | 429 | | Invalid request body | 400 |

임베딩 응답은 단항(unary, 비스트리밍)이에요.

출력 의미와 범위 (Output semantics and scope)

엔드포인트는 모델 출력에 무관(model-output-agnostic) 해요. 모델이 내는 요청별 벡터를 그대로 OpenAI 임베딩 스키마로 직렬화해 반환합니다.

  • 분류기 / 리워드 모델 (예: BERT 시퀀스 분류기): 반환 벡터는 모델의 클래스 로짓/점수 벡터([num_labels])예요.
  • 텍스트 임베딩 모델Qwen3-Embedding 계열(Qwen3-Embedding-0.6B, -4B, -8B)이 지원돼요. 이들은 Qwen3ForCausalLM 디코더에 sentence-transformers 풀링 파이프라인을 더한 형태로 제공되며, 임베딩 서버가 이를 감지해 추가 플래그 없이 L2-정규화된 마지막 토큰 히든 스테이트(각각 1024 / 2560 / 4096 차원의 [hidden_size] 문장 임베딩 벡터)를 서빙해요.

한편 임베딩 경로는 동기 llm.encode() 고속 경로(EncoderExecutor)를 사용하는데, 배치당 단일 forward pass로 KV 캐시·샘플러·디코드 루프가 없어요. 또 서버 인스턴스당 인코더 모델 하나이고, 생성과 임베딩 모드는 한 서버에서 섞이지 않아요. 서버당 싱글 GPU이며 인코드 경로는 in-process로 실행되고 멀티 GPU 워커 프록시를 쓰지 않기 때문에, embeddings 커맨드는 텐서/파이프라인 병렬화 플래그를 노출하지 않습니다(--config 파일이 설정하면 시작 시 명확한 오류로 실패해요). 단일 in-server 워커가 GPU를 구동하는데 (num_workers 손잡이 없음), GPU는 forward를 직렬화하고 기본 executor는 동시 호출에 안전하지 않아요. 처리량을 올리려면 워커를 늘리는 대신 --max_batch_size / --max_queue_delay를 조정해야 합니다.

GPU 간 확장 (Scaling out across GPUs)

임베딩/인코더 전용 모델은 보통 작아서 단일 GPU에 충분히 들어가요. 더 많은 GPU를 쓰는 권장 방법은 그래서 데이터 병렬화예요. GPU마다 싱글 GPU trtllm-serve embeddings 인스턴스를 하나씩 띄우고 앞에 로드 밸런서를 두는 방식입니다.

텐서/파이프라인 병렬화(단일 모델을 여러 GPU에 나누는 것)는 한 GPU에 너무 커서 못 들어가는 임베딩 모델에만 필요한데, 인코더 전용 모델에선 드문 일이에요. embeddings 커맨드는 아직 이를 지원하지 않으며 후속 작업으로 계획되어 있어요.

llm.encode()와의 관계

서버는 내부적으로 기존 Python llm.encode() API(LLM(..., encode_only=True))를 재사용해요. 추가된 것은 비동기 결합(coalescing) 레이어와 HTTP 표면뿐이에요. 동기 llm.encode() API는 직접 Python 호출자에게 그대로 동작합니다.

더 알아보기 (Learn more)