LLM 쉽고 빠르게 서빙하기
요즘은 LLM(Large Language Model)의 시대입니다. 2022년에 ChatGPT가 처음 서비스된 이후로, 하루가 다르게 더 빠르고, 더 정확하고, 심지어 이미지나 비디오, 음성 등을 이해하고 생성하는 모델들이 등장하고 있습니다. 아래 그림처럼, 2024년 들어서 상용 LLM과 오픈소스 LLM이 경쟁적으로 등장하며, 시장을 빠르게 변화시키고 있습니다.
토스증권 역시 이런 흐름에 발맞춰, LLM을 활용한 서비스 혁신에 적극 나서고 있습니다. 투자 데이터를 이해하고 고객들에게 보다 나은 금융 서비스를 제공하기 위해, 우리는 자체적으로 LLM 모델을 학습시키고 이를 서비스에 적용하려는 데 집중하고 있습니다.
구슬이 서 말이라도 꿰어야 보배라는 말처럼, 좋은 LLM을 학습시키는 것도 중요하지만 이걸 서비스에 적용하지 못하면 큰 의미가 없습니다. 하지만 일정 수준의 이상의 성능을 지원하는 LLM을 서비스에 적용할 때 크게 두 가지의 어려움이 있습니다.
첫 번째는 속도의 문제입니다. 중간 사이즈의 LLM인 Llama3 8B 모델을 일반적인 Transformers 패키지를 사용해서 추론하면, 단순한 질문에도 수십 초씩 기다려야 합니다. 따라서, 서비스에 적용할 만큼의 성능을 확보하기 위해서 추론 속도를 빠르게 만들 필요가 있습니다.
두 번째는 사용성의 문제입니다. 어떤 모델이 우리 서비스에 더 적합한지 판단하려면, 다양한 모델을 서비스에 적용하면서 각기 다른 상황에 맞게 테스트해야 합니다. 하지만 학습된 모델을 서빙하기 위해서 개발자는 많은 정보를 알아야 됩니다. 예를 들어, 모델 코드를 컨테이너화하기 위해 필요한 Docker 이미지 빌드 방법이나, 서버를 Kubernetes에 배포하기 위해 필요한 manifest 작성법 등이 있습니다. 이런 허들 때문에 여러 모델을 서빙해서 A/B 테스트로 비교하기 어렵습니다.
이번 아티클에서는 토스증권이 어떻게 LLM의 추론 속도, 모델 서빙 방법을 더 효율적으로 개선했는지 알려드리겠습니다.
문제 1. LLM을 우리 인프라에서 그냥 돌리면 너무 느려요
ChatGPT, Claude, Gemini 등 다양한 LLM 서비스를 사용해보셨나요? 이런 서비스는 속도가 상당히 빠릅니다. 특히 최신 모델들은 그 성능도 대단하지만, 2~3 문단으로 구성된 긴 글을 쓰는데 수 초 정도면 충분합니다. 하지만, 그에 준하는 성능을 보인다고 알려진 Llama3 70B, Mixtral 8x22B 등의 모델들을 써보면, GPU를 사용하더라도 비슷한 양의 토큰을 생성하는 데 수 분, 길게는 수십 분이 걸리는 경우도 있습니다.
이런 모델들이 느린 이유는 autoregressive 모델의 특성 때문입니다. Autoregressive 모델이란, 이전 모델의 결과물을 입력으로 이용해서 다음 모델 결과를 생성하는 모델을 의미합니다. 즉, 각각의 토큰을 순차적으로 생성해야 하기 때문에 속도가 느립니다. 예를 들어, 안녕하세요. 토스 테크 블로그입니다
. 라는 문장은, 안녕하세요. -> 토스 -> 테크 -> 블로그입니다.
라는 순서대로 생성합니다.
이런 본질적인 구조를 개선할 수는 없지만, cache를 이용해서 속도를 빠르게 만들 수 있습니다. LLM 학습에는 Key-value cache(KV cache)를 사용하는데요, 일반적인 캐시와 동일하게 중복 계산을 줄이는 방법입니다. Transformer의 구조상 여태까지 입력으로 들어온 토큰들을 이용해서 self attention을 구성합니다. 위의 예제에서, 테크
라는 단어를 생성할 때 필요한 attention의 key와 value는 안녕하세요.
와 토스
입니다. 블로그입니다.
라는 단어를 생성할 때 필요한 토큰은 안녕하세요.
, 토스
, 테크
입니다.
앞의 두 토큰이 겹치기 때문에, 이 토큰을 가지고 계산해 둔 attention은 캐싱해서 계산량을 줄일 수 있습니다. 이를 KV cache라고 하며, 이를 통해 추론 속도를 O(
n^3
) → O(n^2)
까지 개선할 수 있습니다. 추가적으로, KV cache를 조금 더 메모리 효율적으로 하는 paged KV cache나, CUDA를 더 효율적으로 사용하는 kernel fusion과 같은 방법들이 있습니다.
이런 다양한 개념들을 적용해서, 추론 속도를 빠르게 해주는 tensorrt-llm, vllm, lmdeploy, tgi 등 오픈소스가 여럿 있습니다. 이들 중 어떤 것이든 사용하면, 기존의 transformers 패키지를 그대로 사용했을 때보다 체감상 훨씬 빠른데요, 우리에게 가장 적합한 오픈소스를 어떻게 찾을까요?
오픈소스를 비교할 때 토스증권은 크게 세 가지 항목을 고려했습니다: 정량적 지표, 사용성, 지원하는 모델 종류
. 이런 지표를 고려해서 토스증권의 상황에 가장 맞는 오픈소스를 선택했습니다. 각 항목에 대한 자세한 설명은 아래를 참고해주세요.
- 정량적 지표
- Token per Second (TPS). 초당 토큰 생성 숫자로, generative model 특성상 매 요청마다 생성해야 하는 토큰의 숫자가 매우 다릅니다. 따라서 초당 request 처리 숫자는 변동성이 커서 정확한 비교가 어렵고, 비교 가능한 단위인 토큰 숫자로 비교했습니다.
- Time to First Token (TTFT). 첫 번째 토큰이 생성되기까지 걸리는 시간(초)입니다. 일반적으로 GPU inference를 할 때에는 warm-up 시간이 있어서 첫 토큰을 생성하는 게 오래 걸립니다. 이 지표를 통해, 각 프레임워크 별로 warm-up 시간을 얼마나 단축시켰는지 측정합니다.
- 사용성
- 학습한 모델을 얼마나 적은 단계로 서빙까지 할 수 있는지를 의미합니다. 예를 들어, ONNX와 같이 컴파일 과정을 거쳐야 하는 것은 사용성이 좋지 않다고 판단했습니다.
- 지원하는 모델 종류
- 자주 사용되는 LLM architecture를 지원하는지를 확인했습니다. 흔히 pretrained 모델로 자주 채용되는 Llama, Gemma, Mixtral, Qwen 등을 모두 지원하는지 평가했습니다.
위 표는 토스증권에서 직접 각 오픈소스를 실험한 결과입니다. 여러 아키텍처에 대해서 테스트해봤을 때, 종합적으로 정량적 지표 면에서 빠른 편에 속하면서, 사용성도 편하고, 다양한 모델을 지원하는 vllm을 선택했습니다.
또한 vllm을 서빙할 프레임워크로는 Triton Inference Server(TIS)를 선택했는데요. 가장 범용적으로 많이 사용되고 dynamic batching, continuous batching 등 throughput을 올려줄 수 있는 다양한 기능도 제공해주기 때문입니다. 또한, vllm을 사용하기 위한 backend도 직접 오픈소스로 제공하는 장점이 있습니다.
적합한 오픈소스를 찾고 적용하면 LLM의 추론 속도를 큰 폭으로 높일 수 있습니다. 하지만, 여전히 이 모델을 서비스로 만들기까지는 여러 난관이 가로막고 있습니다. 다음 단락에서, 어떻게 하면 모델을 쉽게 서빙하고 운영할 수 있는지 설명드릴게요.
문제 2. K8S에서 서버를 띄우는 것이 어려워요
토스증권에서는 Kubernetes(K8s)라는 오픈소스 플랫폼을 이용해서 여러 서비스들을 관리하고 있습니다. LLM도 예외는 아닌데요. K8s가 매우 편리하고 다양한 장점이 있는 도구임은 사실이지만 처음 접하는 사용자는 배워야할 것들이 상당히 많습니다.
예를 들어, 단순히 hello world
를 출력하는 간단한 서버를 띄울 때에도, 배포용 Helm Charts를 작성해야 합니다. 여기에는 서버 코드가 들어있는 Docker 이미지와, 이를 실행시키는 데 필요한 명령어가 들어있는 deployment
, 외부에서 서버로 트래픽을 연결시키는 virtualservice
, service
등 여러 파일(manifest
)들이 필요하며, 각각에 대해서 어느 정도 이해해야 합니다.
게다가 토스증권에서 선택한 vllm과 이를 잘 띄워주는 Triton을 사용하려면 더 많은 지식과 경험이 필요합니다. 모델을 vllm + Triton에서 구동할 수 있게 필요한 서버 코드, 생성 모델을 실행할 때 필요한 configuration(사용할 KV cache의 크기, GPU의 숫자, 모델이 생성 가능한 최대 길이 등) 등을 정의해주어야 하며, 이런 코드를 Docker 이미지로 빌드할 수 있어야 합니다.
즉, 하나의 모델을 잘 학습했더라도 실제 서비스로 내보내기 위해서는 모델뿐만 아니라 vllm, Triton, Docker, K8s에 대한 전반적인 지식이 필요한 셈이죠. 특히나, 모델을 적용할 서비스와 모델러의 숫자가 늘어날수록 이런 학습 비용은 더더욱 증가하게 됩니다.
전체적인 배포 플로우를 도식화하면 아래와 같은데요. 먼저 LLM inference 코드를 작성하고, 이를 Docker 이미지로 빌드합니다. 그리고 서버 구동에 필요한 yaml 파일들을 작성한 뒤, ArgoCD라는 툴을 이용해서 K8s에 배포합니다.
토스증권에서는 이 문제를 각각 나눠서 해결했습니다. 공통화된 라이브러리와 코드를 작성해서 여러 서비스가 하나의 Docker 이미지를 공유하게 구성했고, 이전과 다르게 K8s, Helm Charts, ArgoCD에 대한 지식이 거의 없더라고 쉽게 모델을 서빙할 수 있게 배포 과정을 개선했습니다.
먼저 LLM Inference 코드는 Triton에서 제공해주는 vllm_backends라는 오픈소스를 활용했습니다. vllm의 구성(configuration)과 모델 경로를 단 하나의 파일로 정의하면, Triton 서버에서 간단히 실행할 수 있게 돕습니다.
이를 바탕으로, 토스증권의 인프라에 맞도록 model registry를 구성하고, 필요한 configuration를 변수화해서, 하나의 Docker 이미지를 여러 서비스가 공유할 수 있도록 최적화했습니다. 이제 모델러들이 별도의 Docker 이미지를 빌드하지 않고도,다양한 모델, 다양한 구성으로 LLM 서버를 구동할 수 있습니다.
마지막으로 저희 팀에서는 Kserve라는 패키지를 도입해서 배포 과정을 더욱 간소화했습니다. Kserve는 Kubeflow 위에 올라가는 서버리스 플랫폼으로, 머신러닝 모델을 쉽게 배포하고 관리할 수 있게 도와줍니다. 이로써 복잡한 MLOps 작업 없이, 모델러들이 yaml 파일 하나만 작성하면 서비스를 배포할 수 있게 만들었습니다.
여기까지 해서, LLM 서버를 띄우는 과정이 간소화되었습니다. 기존처럼 MLOps 지식이 필요하거나, 많은 단계를 거쳐야 하지 않고, 아래와 같은 형태의 yaml만 작성하면 모델을 서빙할 수 있는 형태로 바뀌었습니다. 학습시킨 모델의 이름을 넣어주고, 몇 가지 configuration(GPU 몇 개 사용할지 등)을 입력해서 yaml 파일을 만든 뒤, 이걸 제출하면 바로 서버를 띄울 수 있습니다.
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: huggingface-llama3
spec:
predictor:
volumes:
- name: shmdir
emptyDir:
medium: Memory
sizeLimit: "64G"
triton:
name: triton-vllm
image: PRIVATE_IMAGE # 공용으로 사용하는 Docker 이미지
command:
- ./SOME_SCRIPT.sh meta-llama/Meta-Llama-3-8B-Instruct # configuration (모델 명 등)
volumeMounts:
- mountPath: /dev/shm
name: shmdir
resources:
requests:
nvidia.com/gpu: "1" # 사용하고자 하는 자원 (gpu 숫자)
이제 서버를 띄웠으니, 내가 만든 서버가 잘 동작하는지, latency나 throughput이 줄어드는지 등을 체크해봐야합니다. Kserve + Triton을 사용하면 정형화된 형태로 로그를 남기기 때문에, 별다른 노력 없이 대시보드에 새로 생성된 서버를 추가할 수 있습니다.
아래 그림처럼, TPS나 latency를 그라파나에서 시각적으로 확인이 가능하며, 알림 설정도 쉽게 해서 오류가 발생했을 때 즉각적으로 대처할 수 있습니다.
이제 새로운 pretrained 모델이 나오거나, 간단하게 학습해 본 모델을 쉽게 서빙해서 테스트할 수 있는 환경이 갖춰졌습니다. 이를 통해서, 기존에는 쉽게 하기 힘들었던 Llama 70B와 우리 모델의 성능 벤치마크도 가능해졌고, 큰 모델을 이용해서 양질의 데이터 구축도 쉽게 할 수 있게 되었습니다.
LLM 기술은 단순히 더 나은 모델을 만드는 것을 넘어, 실제 서비스에 어떻게 연결하느냐에 따라 그 가치가 결정됩니다. 토스증권의 사례는 이러한 기술적 과제를 실용적으로 풀어나가는 다양한 방향성 중에 하나입니다. 여러분도 자신만의 환경과 목표에 맞는 최적의 방법을 찾아가며, LLM의 가능성을 확장해 나가길 바랍니다.