주요 컨텐츠로 이동

LLM 추론 성능 엔지니어링: 모범 사례


이 포스트 공유하기
LLM Inference Performance Engineering: Best Practices

(번역: Youngkyong Ko) Original Blog Post

이 블로그에서 MosaicML 엔지니어링 팀은 프로덕션용으로 널리 사용되는 오픈 소스 대규모 언어 모델(LLM)을 활용하는 방법에 대한 모범 사례를 공유합니다. 또한 이러한 모델을 기반으로 구축된 추론 서비스를 배포하는 가이드라인을 제공하여 사용자가 모델과 배포 하드웨어를 선택하는 데 도움을 드리고자 합니다. 이러한 가이드라인은 프로덕션 환경에서 여러 PyTorch 기반 백엔드를 사용해 본 경험에서 도출한 것으로, FasterTransformers, vLLM, 곧 출시될 NVIDIA의 TensorRT-LLM 등에 대한 경험을 바탕으로 합니다.

LLM 텍스트 생성에 대한 이해

LLM(대규모 언어 모델)은 2단계 프로세스로 텍스트를 생성합니다: 입력 프롬프트의 토큰을 병렬로 처리하는 '미리 채우기(prefill)'와 자동 회귀 방식으로 한 번에 하나의 '토큰'씩 텍스트를 생성하는 "디코딩(decoding)"이 그것입니다. 생성된 각 토큰은 입력에 추가되고 모델에 다시 피드백되어 다음 토큰을 생성합니다. LLM이 특별한 중지 토큰을 출력하거나 사용자 정의 조건이 충족되면(예: 최대 토큰 수에 도달한 경우) 생성이 중지됩니다. LLM이 디코더 블록을 사용하는 방법에 대한 자세한 배경 지식이 필요하면 이 블로그 게시물을 참조하세요.

토큰은 단어 또는 서브워드(sub-words)일 수 있으며, 텍스트를 토큰으로 분할하는 정확한 규칙은 모델마다 다릅니다. 예를 들어, Llama 모델이 텍스트를 토큰화하는 방식OpenAI 모델이 텍스트를 토큰화하는 방식을 비교해 볼 수 있습니다. LLM 추론 서비스 제공업체는 종종 토큰 기반 메트릭(예: 초당 토큰 수)의 성능에 대해 이야기하지만, 이러한 토큰화 방식의 차이 때문에, 위와 같은 모델 유형 간 성능 수치 비교가 항상 가능한 것은 아닙니다. 구체적인 예로, 애니스케일 팀은 Llama 2 토큰화가 ChatGPT 토큰화보다 19% 더 오래 걸리지만 전체 비용은 훨씬 더 낮다는 사실을 발견했습니다. 또한 HuggingFace의 연구원들은 동일한 양의 텍스트를 훈련하는 데 Llama 2가 GPT-4보다  ~20% 더 많은 토큰을 필요로 한다는 사실을 발견했습니다.

LLM 서빙의 중요 지표

그럼 추론 속도에 대해 정확히 이해하려먼 어떻게 해야 할까요?

우리 팀은 LLM 서빙을 위해 네 가지 주요 지표를 사용합니다:

  1. 첫 번째 토큰까지의 시간(Time to First Token, TTFT): 사용자가 쿼리를 입력한 후 모델의 결과물을 얼마나 빨리 볼 수 있는지 측정합니다. 실시간 상호작용에서는 응답 대기 시간이 짧아야 하지만 오프라인 워크로드에서는 그다지 중요하지 않습니다. 이 지표는 프롬프트를 처리한 다음 첫 번째 출력 토큰을 생성하는 데 필요한 시간에 따라 결정됩니다.
  2. 출력 토큰당 시간(Time Per Output Token, TPOT): 시스템을 쿼리하는 각 사용자에 대해 출력 토큰을 생성하는 데 걸리는 시간입니다. 이 지표는 각 사용자가 모델의 "속도"를 어떻게 인지하는지에 해당합니다. 예를 들어, 100 milliseconds/tok의 TPOT는 사용자당 초당 10개의 토큰, 즉 분당 ~450단어에 해당하며, 이는 일반적인 사람이 읽을 수 있는 속도보다 빠릅니다.
  3. 지연 시간(Latency): 모델이 사용자에 대한 전체 응답을 생성하는 데 걸리는 총 시간입니다. 총 응답 지연 시간은 앞의 두 지표를 사용하여 계산할 수 있습니다: 지연 시간 = (TTFT) + (TPOT) * (생성할 토큰 수).
  4. 처리량(Throughput): 추론 서버가 모든 사용자와 요청에 대해 생성할 수 있는 초당 출력 토큰의 수입니다.

우리의 목표는? 가장 빠른 첫 번째 토큰 생성 시간, 가장 높은 처리량, 출력 토큰당 가장 빠른 시간입니다. 다시 말해, 최대한 많은 사용자를 위해 가능한 한 빠르게 텍스트를 생성하는 모델을 만들고자 합니다.

특히 처리량과 출력 토큰당 시간 사이에는 상충 관계(tradeoff)가 있습니다: 16개의 사용자 쿼리를 동시에 처리하면 쿼리를 순차적으로 실행할 때보다 처리량은 높아지지만 각 사용자에 대한 출력 토큰을 생성하는 데 시간이 더 오래 걸립니다.

총 추론 지연 시간 목표가 있는 경우에 모델을 평가하는 데 유용한 몇 가지 휴리스틱을 소개합니다:

  • 출력 길이가 총 응답 지연 시간을 결정합니다: 평균 지연 시간의 경우 일반적으로 예상/최대 출력 토큰 길이에 모델의 출력 토큰당 전체 평균 시간을 곱하면 바로 구할 수 있습니다.
  • 입력 길이는 성능에는 중요하지 않지만 하드웨어 요구 사항에는 중요합니다: MPT 모델에서 512개의 입력 토큰을 추가하는 것이 8개의 출력 토큰을 추가 생성하는 것보다 지연 시간 증가에 미치는 영향이 더 적습니다. 그러나 긴 입력을 지원해야 하는 경우 모델 서빙이 더 어려워질 수 있습니다. 예를 들어, 최대 컨텍스트 길이가 2048개인 MPT-7B를 서비스하려면 A100-80GB(또는 그 이상)를 사용하는 것이 좋습니다.
  • 전체 지연 시간은 모델 크기에 따라 비선형적으로 증가합니다: 동일한 하드웨어에서 더 큰 모델이 더 느리지만, 속도 비율이 반드시 매개변수 수의 비율과 일치하지는 않습니다. MPT-30B 지연 시간은 MPT-7B 지연 시간의 ~2.5배입니다. Llama2-70B 지연 시간은 Llama2-13B 지연 시간의 ~2배입니다.

잠재 고객으로부터 평균 추론 지연 시간을 제공해 달라는 요청을 종종 받습니다. 특정 지연 시간 목표("토큰당 20ms 미만이 필요합니다")를 정하기 전에, 예상되는 입력 및 원하는 출력 길이의 특성을 찬찬히 검토해 보는 것이 좋습니다.

LLM 추론의 도전 과제

LLM 추론을 최적화하는데 다음과 같은 일반적인 기술을 활용할 수 있습니다:

  • 연산자 결합(Operator Fusion): 인접한 서로 다른 연산자를 결합하면 지연 시간이 개선되는 경우가 많습니다.
  • 양자화(Quantization): 활성화와 가중치를 압축하여 더 적은 수의 비트를 사용합니다.
  • 압축(Compression): 희소성(Sparsity) 또는 증류(Distillation).
  • 병렬화(Parallelization): 여러 장치에 걸친 텐서 병렬 처리 또는 대규모 모델의 경우 파이프라인 병렬 처리

이러한 방법 외에도 Transformer 별로 여러 가지 중요한 최적화 방법이 있습니다. 대표적인 예가 KV(키-값) 캐싱입니다. 디코더 전용 트랜스포머 기반 모델의 Attention 메커니즘은 계산 측면에서 비효율적입니다. 각 토큰은 이전에 본 모든 토큰에 주의를 기울이기 때문에 새로운 토큰이 생성될 때마다 많은 동일값들을 다시 계산해야 합니다. 예를 들어, N번째 토큰을 생성하는 동안 (N-1)번째 토큰은 (N-2)번째, (N-3)번째 ... 첫 번째 토큰을 참조합니다. 마찬가지로 (N+1)번째 토큰을 생성하는 동안 N번째 토큰에 대한 관심은 다시 (N-1)번째, (N-2)번째, (N-3)번째, ... 첫 번째 토큰을 살펴봐야 합니다. KV 캐싱, 즉 관심 계층에 대한 중간 키/값을 저장하는 것은 이러한 결과를 나중에 재사용할 수 있도록 보존하여 반복적인 계산을 피하기 위해 사용됩니다.

핵심은 메모리 대역폭 

LLM의 계산은 주로 행렬-행렬 곱셈 연산이 주를 이루며, 낮은 차원의 행렬 곱셈 연산은 일반적으로 대부분의 하드웨어에서 메모리 대역폭에 바운드됩니다. 자동 회귀 방식으로 토큰을 생성할 때, 작은 배치 크기에서는 활성화 행렬 차원 중 하나(배치 크기와 시퀀스의 토큰 수로 정의됨)가 작아집니다. 따라서 속도는 로드된 데이터를 얼마나 빨리 계산할 수 있는지가 아니라, 모델 파라미터를 GPU 메모리에서 로컬 캐시/레지스터로 얼마나 빨리 로드할 수 있는지에 따라 달라집니다. 추론 하드웨어에서 사용 가능한 메모리 대역폭과 확보된 메모리 대역폭은 최대 컴퓨팅 성능보다 토큰 생성 속도를 더 잘 예측할 수 있는 지표입니다.

추론 하드웨어 활용률(utilization)는 서비스 비용 측면에서 매우 중요합니다. GPU는 비싸기 때문에 가능한 한 많은 작업을 수행하도록 하여 활용도를 높이고자 합니다. 공유 추론 서비스(Shared inference service)는 많은 사용자의 워크로드를 결합하고, 개별 워크로드간 차이를 메우고, 중복되는 요청을 일괄 처리함으로써 비용을 낮출 수 있습니다. Llama2-70B와 같은 대형 모델의 경우, 대규모 배치 크기에서만 우수한 비용 대비 성능을 달성할 수 있습니다. 대규모 배치 크기에서 작동할 수 있는 추론 서빙 시스템을 갖추는 것은 비용 효율성을 위해 매우 중요합니다. 그러나 배치 크기가 크다는 것은 KV 캐시 크기가 커진다는 것을 의미하며, 이는 곧 모델 서빙에 필요한 GPU의 수가 증가한다는 것을 의미합니다. 여기에는 줄다리기가 존재하며, 공유 추론 서비스 운영자는 비용 절충안을 마련하고 시스템 최적화를 구현해야 합니다.

모델 대역폭 활용률 (Model Bandwidth Utilization, MBU)

LLM 추론 서버는 얼마나 최적화되어 있을까요?

앞서 간략하게 설명했듯이, (특히 디코드 단계에) 배치 크기가 작은 경우 LLM 추론에서는, 모델 파라미터를 디바이스 메모리에서 컴퓨팅 유닛으로 얼마나 빨리 로드할 수 있는지에 따라 병목 현상이 발생합니다. 메모리 대역폭은 데이터 이동 속도를 결정합니다. 우리는 하드웨어의 활용도를 측정하기 위해 모델 대역폭 활용률(MBU)이라는 새로운 지표를 도입했습니다. MBU는 (확보된 메모리 대역폭) / (피크 메모리 대역폭)으로 정의되며, 여기서 확보된 메모리 대역폭은 ((총 모델 파라미터 크기 + KV 캐시 크기) / TPOT)입니다.

예를 들어, 16비트 정밀도로 실행되는 7B 매개변수의 TPOT가 14ms인 경우, 14ms에 14GB의 매개변수를 이동하는 것이므로 초당 1TB의 대역폭을 사용하는 것입니다. 머신의 피크 대역폭이 초당 2TB인 경우, 50%의 MBU에서 실행되고 있는 것입니다. 간단하게 설명하기 위해 이 예제에서는 배치 크기가 작고 시퀀스 길이가 짧을 경우 작은 KV 캐시 크기를 무시합니다. MBU 값이 100%에 가까우면 추론 시스템이 사용 가능한 메모리 대역폭을 효과적으로 활용하고 있음을 의미합니다. MBU는 서로 다른 추론 시스템(하드웨어 + 소프트웨어)을 정규화된 방식으로 비교하는 데에도 유용합니다. MBU는 컴퓨팅 바운드 설정(compute-bound settings)에서 중요한 모델 플롭 활용도(MFU; PaLM paper에서 도입됨) 메트릭을 보완합니다.

그림 1은 roofline plot과 유사한 그래프에서 MBU를 그림으로 표현한 것입니다. 주황색 음영 영역의 비스듬한 실선은 메모리 대역폭이 100%로 완전히 수렴할 때 가능한 최대 처리량을 나타냅니다. 그러나 실제로는 배치 크기가 작은 경우(흰색 점)에는 관찰된 성능이 최대치보다 낮으며, 얼마나 낮은지가 MBU의 척도가 됩니다. 배치 크기가 큰 경우(노란색 영역)에는 시스템이 컴퓨팅 바인딩되며, 최대 가능한 처리량의 일부로서 달성된 처리량은 모델 플롭 활용도(MFU)로 측정됩니다.

Model Bandwidth Utilization
그림 1: MBU(모델 대역폭 활용률)와 MFU(모델 플롭 활용률)를 보여줍니다. MBU와 MFU는 각각 메모리 바운드 영역과 컴퓨팅 바운드 영역에서 달성한 피크의 비율입니다.

MBU와 MFU는 주어진 하드웨어 설정에서 추론 속도를 더 높일 수 있는 여지를 얼마나 더 확보할 수 있을지 결정합니다. 그림 2는 TensorRT-LLM 기반 추론 서버에서 다양한 수준의 텐서 병렬 처리 정도에 따라 측정된 MBU를 보여줍니다. 최대 메모리 대역폭 활용률은 연속된 대용량 메모리 청크(chunk)를 전송할 때 달성됩니다. MPT-7B와 같은 더 작은 모델을 여러 GPU에 분산할 경우, 각 GPU에서 더 작은 메모리 청크를 이동하기 때문에 MBU가 낮아지는 것을 볼 수 있습니다.

Empirically observed MBU
그림 2: A100-40G GPU에서 TensorRT-LLM을 사용하여 다양한 수준의 텐서 병렬 처리에 대한 MBU를 경험적으로 관찰한 결과입니다. 요청: 배치 크기가 1인 512개의 입력 토큰 시퀀스.

그림 3은 NVIDIA H100 GPU에서 다양한 수준의 텐서 병렬 처리 및 배치 크기에 대해 경험적으로 관찰된 MBU를 보여줍니다. 배치 크기가 증가함에 따라 MBU는 감소합니다. 그러나 GPU를 확장할수록 MBU의 상대적 감소는 덜합니다. 메모리 대역폭이 더 큰 하드웨어를 선택하면 더 적은 수의 GPU로 성능을 향상시킬 수 있다는 점도 주목할 가치가 있습니다. 배치 크기 1의 경우, 2xH100-80GB에서는 60%의 더 높은 MBU를 달성할 수 있는 반면, 4xA100-40GB GPU에서는 55%에 그칩니다(그림 2).

Tensor Parallelism Modes
그림 3: H100-80G GPU에서 다양한 배치 크기와 텐서 병렬 처리 모드에 대해 경험적으로 관찰된 MBU 입니다. 요청: 512개의 입력 토큰 시퀀스

벤치마크 결과

지연 시간

MPT-7B와 Llama2-70B 모델의 다양한 텐서 병렬 처리 정도에 따라 첫 번째 토큰에 걸리는 시간(TTFT)과 출력 토큰당 시간(TPOT)을 측정했습니다. 입력 프롬프트가 길어질수록 첫 번째 토큰을 생성하는 데 걸리는 시간이 전체 지연 시간의 상당 부분을 차지하기 시작합니다. 여러 GPU에서 텐서를 병렬 처리하면 이러한 지연 시간을 줄이는 데 도움이 됩니다.

모델 훈련과는 달리, GPU를 늘려감에 따라 추론 지연 시간에는 수익 체감(diminishing returns) 현상이 발생합니다. 예를 들어, Llama2-70B의 경우 GPU를 4배에서 8배로 늘리면 작은 배치 크기에서는 지연 시간이 0.7배만 감소합니다. 그 이유 중 하나는 병렬 처리가 높을수록 MBU가 낮아지기 때문입니다(앞서 설명한 대로). 또 다른 이유는 텐서 병렬 처리로 인해 GPU 노드 전체에 통신 오버헤드가 발생하기 때문입니다.

 Time to first token (ms)
Model1xA100-40GB2xA100-40GB4xA100-40GB8xA100-40GB
MPT-7B46 (1x)34 (0.73x)26 (0.56x)-
Llama2-70BDoesn't fit154 (1x)114 (0.74x)

표 1: 512 토큰 길이, 배치 크기는 1의 입력 요청이 주어졌을 때 첫 번째 토큰에 걸리는 시간. Llama2 70B와 같은 대형 모델에는 메모리 맞춤 설정을 위해 최소 4xA100-40B GPU가 필요합니다.

배치 크기가 커지면 텐서 병렬 처리가 높을수록 토큰 지연 시간이 상대적으로 더 크게 감소합니다. 그림 4는 MPT-7B의 출력 토큰당 시간이 어떻게 달라지는지 보여줍니다. 배치 크기 1에서는 2x에서 4x로 증가하면 토큰 지연 시간은 약 12%만 감소합니다. 배치 크기 16에서는 4x의 지연 시간이 2x보다 33% 더 짧습니다. 이는 배치 크기 1에 비해 배치 크기 16의 텐서 병렬 처리 수준이 높을수록 MBU의 상대적 감소가 더 작다는 것으로, 앞서 관찰한 결과와 일치합니다.

Increasing Number of GPUs
그림 4: A100-40GB GPU에서 MPT-7B를 확장할 때 사용자당 출력 토큰당 시간. 지연 시간은 GPU 수가 증가함에 따라 선형적으로 증가하지 않습니다. 요청: 128개의 입력 토큰과 64개의 출력 토큰으로 구성된 시퀀스

그림 5는 4x에서 8x 사이의 상대적 개선이 덜 뚜렷하다는 점을 제외하면 Llama2-70B의 비슷한 결과를 보여줍니다. 또한 서로 다른 두 하드웨어의 GPU 스케일링을 비교했습니다. H100-80GB는 A100-40GB에 비해 GPU 메모리 대역폭이 2.15배이므로 4x 시스템의 경우 배치 크기 1에서 지연 시간이 36%, 배치 크기 16에서 52% 더 낮음을 볼 수 있습니다.

Multiple GPUs
그림 5: 여러 GPU에 걸쳐 Llama-v2-70B를 확장할 때 사용자당 출력 토큰당 시간(입력 요청: 512 토큰 길이). 1x40GB GPU, 2x40GB 및 1x80GB GPU 숫자가 누락된 것은 해당 시스템에서 Llama-v2-70B(float16)가 맞지 않기 때문입니다.

처리량(Throughput)

요청을 일괄 처리함으로써 토큰당 처리량과 시간을 절충할 수 있습니다. GPU 평가 중에 쿼리를 그룹화하면 쿼리를 순차적으로 처리할 때보다 처리량이 증가하지만, 각 쿼리를 완료하는 데 시간이 더 오래 걸립니다(대기열 효과 무시).

추론 요청을 일괄 처리하는 몇 가지 일반적인 기술이 있습니다:

  • 정적 일괄 처리(Static batching): 클라이언트는 여러 프롬프트를 요청에 담고, 일괄 처리의 모든 시퀀스가 완료된 후 응답이 반환됩니다. 추론 서버는 이를 지원하지만 반드시 필요한 것은 아닙니다.
  • 동적 일괄 처리(Dynamic batching): 프롬프트가 서버 내부에서 즉시 일괄 처리됩니다. 일반적으로 이 방법은 정적 일괄 처리보다 성능이 떨어지지만 응답이 짧거나 길이가 균일한 경우 최적에 가까워질 수 있습니다. 요청에 서로 다른 매개 변수(parameters)가 있는 경우 제대로 작동하지 않습니다.
  • 연속 일괄 처리(Continuous batching): 요청이 도착할 때 함께 일괄 처리하는 아이디어는 이 훌륭한 논문에서 소개되었으며 현재 SOTA 방식으로 사용되고 있습니다. 일괄 처리의 모든 시퀀스가 완료될 때까지 기다리는 대신, 반복 수준에서 시퀀스들을 그룹화합니다. 동적 일괄 처리보다 10배~20배 더 나은 처리량을 달성할 수 있습니다.

LLM Serving
그림 6: LLM 서빙에서의 다양한 일괄 처리 방식. 일괄 처리는 추론의 효율성을 개선하는 효과적인 방법입니다.

일반적으로 공유 서비스에는 연속 일괄 처리가 가장 좋은 방법이지만, 다른 두 가지 방법이 더 나은 경우도 있습니다. QPS가 낮은 환경에서는 동적 배칭이 연속 배칭보다 더 나은 성능을 발휘할 수 있습니다. 때로는 더 간단한 배치 프레임워크에서 낮은 수준의 GPU 최적화를 구현하는 것이 더 쉬울 수 있습니다. 오프라인 배치 추론 워크로드의 경우 정적 배치를 사용하면 상당한 오버헤드를 피하고 더 나은 처리량을 달성할 수 있습니다.

일괄처리 크기(Batch Size)

일괄 처리가 얼마나 잘 작동하는지는 요청 스트림에 따라 크게 달라집니다. 하지만 균일한 요청으로 정적 일괄 처리를 벤치마킹하면 성능의 상한선을 파악할 수 있습니다.

 Batch size
Hardware148163264128
1 x A100.4 (1x)1.4 (3.5x)2.3 (6x)3.5 (9x)OOM (Out of Memory) error
2 x A100.82.54.07.08.0 
1 x A1000.9 (1x)3.2 (3.5x)5.3 (6x)8.0 (9x)10.5 (12x)12.5 (14x) 
2 x A1001.33.05.59.514.517.022.0
4 x A1001.76.211.518.025.033.036.5

테이블 2: 정적 일괄 처리 및 FasterTransformers 기반 백엔드를 사용한 최대 MPT-7B 처리량(요청/초). 요청: 512개의 입력 토큰과 64개의 출력 토큰. 더 큰 입력의 경우, OOM 경계는 더 작은 배치 크기로 설정됩니다.

지연시간 절충안 (Latency Trade-Off)

요청 지연 시간은 배치 크기에 따라 증가합니다. 예를 들어, 배치 크기를 64로 최대화하여 처리량을 극대화하는 경우, NVIDIA A100 GPU 1대를 사용하면 지연 시간은 4배 증가하고 처리량은 14배 증가합니다. 공유 추론 서비스에서는 일반적으로 균형 잡힌 배치 크기를 선택합니다. 자체 모델을 호스팅하는 사용자는 애플리케이션에 적합한 지연 시간/처리량 절충안을 결정해야 합니다. 챗봇과 같은 일부 애플리케이션에서는 빠른 응답을 위한 짧은 지연 시간이 최우선 순위입니다. 비정형 PDF의 일괄 처리와 같은 다른 애플리케이션에서는 개별 문서 처리 지연 시간을 희생하여 모든 문서를 병렬로 빠르게 처리하는 것이 더 중요할 수 있습니다.

그림 7은 7B 모델의 처리량 대 지연 시간 곡선을 보여줍니다. 이 곡선의 각 선은 배치 크기를 1에서 256으로 늘림으로써 얻을 수 있습니다. 이는 다양한 지연 시간 제약 조건에 따라 배치 크기를 얼마나 크게 만들 수 있는지 결정하는 데 유용합니다. 위의 roofline plot을 상기해 보면 이러한 측정값이 예상했던 것과 일치한다는 것을 알 수 있습니다. 특정 배치 크기 이후, 즉 컴퓨팅 바운드 영역으로 넘어가면 배치 크기를 두 배로 늘릴 때마다 처리량은 증가하지 않고 지연 시간만 증가합니다.

Latency Curve
그림 7: MPT-7B 모델의 처리량 지연 시간 곡선. 이를 통해 사용자는 지연 시간 제약 조건에서 처리량 요구 사항을 충족하는 하드웨어 구성을 선택할 수 있습니다.

병렬 처리를 사용할 때는 저수준의 하드웨어 세부 사항을 이해하는 것이 중요합니다. 예를 들어, 클라우드에 따라 모든 8xA100 인스턴스가 동일하지는 않습니다. 일부 서버는 모든 GPU 간에 높은 대역폭으로 연결되는 반면, 다른 서버는 GPU를 pair으로 연결하고 pair 간의 대역폭이 낮습니다. 이로 인해 병목 현상이 발생하여 실제 성능이 위의 곡선에서 크게 벗어날 수 있습니다.

최적화 사례 연구: 양자화(Quantization)

양자화는 LLM 추론에 필요한 하드웨어 요구 사항을 줄이는 데 사용되는 일반적인 기술입니다. 추론 중 모델 가중치 및 활성화의 정밀도를 낮추면 하드웨어 요구 사항을 크게 줄일 수 있습니다. 예를 들어, 16비트 가중치에서 8비트 가중치로 전환하면 메모리가 제한된 환경(예: A100의 Llama2-70B)에서 필요한 GPU 수를 절반으로 줄일 수 있습니다. 4비트 가중치로 낮추면 소비자 하드웨어에서 추론을 실행할 수 있습니다(예: 맥북의 Llama2-70B).

우리의 경험상, 양자화는 신중하게 구현해야 합니다. 나이브한 양자화 기술은 모델 품질을 크게 저하시킬 수 있습니다. 또한 양자화의 영향은 모델 아키텍처(예: MPT vs Llama)와 크기에 따라 달라집니다. 이에 대해서는 향후 블로그 게시물에서 더 자세히 살펴보겠습니다.

양자화와 같은 기술을 실험할 때는 Mosaic Eval Gauntlet과 같은 LLM 품질 벤치마크를 사용해 모델 품질만 따로 평가하는 것이 아니라 추론 시스템의 품질도 함께 평가하는 것이 좋습니다. 또한, 보다 심층적인 시스템 최적화를 모색하는 것도 중요합니다. 특히, 양자화를 통해 KV 캐시를 훨씬 더 효율적으로 만들 수 있습니다.

앞서 언급했듯이, 자동 회귀 토큰 생성에서는 관심 계층의 과거 키/값(KV)을 매 단계마다 다시 계산하는 대신 캐시합니다. KV 캐시의 크기는 한 번에 처리되는 시퀀스의 수와 이러한 시퀀스의 길이에 따라 달라집니다. 더욱이, 다음 토큰 생성이 반복될 때마다 새로운 KV 항목이 기존 캐시에 추가되어 새 토큰이 생성될 때마다 캐시가 커집니다. 따라서 이러한 새로운 값을 추가할 때 KV 캐시 메모리를 효과적으로 관리하는 것은 추론 성능을 향상시키는 데 매우 중요합니다. Llama2 모델은 Grouped Query Attention(GQA)라는 attention의 변형을 사용합니다. KV 헤드의 수가 1인 경우, GQA는 Multi-Query-Attention(MQA)와 동일하다는 점에 유의하세요. GQA는 키/값을 공유하여 KV 캐시 크기를 줄이는 데 도움이 됩니다. KV 캐시 크기를 계산하는 공식은 다음과 같습니다.

batch_size * seqlen * (d_model/n_heads) * n_layers * 2 (K and V) * 2 (bytes per Float16) * n_kv_heads

표 3은 1024 토큰의 시퀀스 길이에서 다양한 배치 크기로 계산한 GQA KV 캐시 크기를 보여줍니다. 이에 비해 Llama2 모델의 파라미터 크기는 70B 모델의 경우 140GB(Float16)입니다. KV 캐시의 양자화는 (GQA/MQA와 더불어) KV 캐시 크기를 줄이기 위한 또 다른 기법이며, 생성 품질에 미치는 영향을 활발하게 평가하고 있습니다.

Batch SizeGQA KV cache memory (FP16)GQA KV cache memory (Int8)
1.312 GiB.156 GiB
165 GiB2.5 GiB
3210 GiB5 GiB
6420 GiB10 GiB

테이블 3: 시퀀스 길이 1024에서 Llama-2-70B의 KV 캐시 크기

앞서 언급했듯이, 낮은 배치 크기에서 LLM을 사용한 토큰 생성은 GPU 메모리 대역폭에 바운드됩니다. 즉, 생성 속도는 모델 파라미터를 GPU 메모리에서 온칩 캐시로 얼마나 빨리 옮길 수 있는지에 따라 달라집니다. 모델 가중치를 FP16(2바이트)에서 INT8(1바이트) 또는 INT4(0.5바이트)로 변환하면 더 적은 데이터를 이동해야 하므로 토큰 생성 속도가 빨라집니다. 그러나 양자화는 모델 생성 품질에 부정적인 영향을 미칠 수 있습니다. 현재 Model Gauntlet을 사용해 모델 품질에 미치는 영향을 평가 중이며, 곧 이에 대한 후속 블로그 포스팅을 게시할 계획입니다.

결론 및 주요 결과

위에서 설명한 각 요소는 우리가 모델을 구축하고 배포하는 방식에 영향을 미칩니다. 이러한 결과를 바탕으로 우리는 하드웨어 유형, 소프트웨어 스택, 모델 아키텍처, 일반적인 사용 패턴을 고려한 데이터 기반 의사결정을 내립니다. 다음은 그간의 경험을 바탕으로 도출한 몇 가지 권장 사항입니다.

최적화 목표를 파악하세요: 인터랙티브 성능에 관심이 있으신가요? 처리량 극대화를 원하시나요? 비용 최소화? 여기에는 예측 가능한 트레이드오프가 있습니다.

지연 시간의 구성 요소에 주목하세요: 대화형 애플리케이션의 경우 첫 번째 토큰에 도달하는 시간이 서비스의 체감 반응성을 결정하고, 출력당 토큰 시간이 서비스의 체감 속도를 결정합니다.

메모리 대역폭이 핵심입니다: 첫 번째 토큰을 생성하는 것은 일반적으로 컴퓨팅 바운드인 반면, 이후의 디코딩은 메모리 바운드 작업입니다. LLM 추론은 종종 메모리 바운드 설정에서 작동하기 때문에 MBU는 최적화에 유용한 지표이며 추론 시스템의 효율성을 비교하는 데 사용할 수 있습니다.

일괄 처리가 매우 중요합니다: 여러 요청을 동시에 처리하는 것은 높은 처리량을 달성하고 고가의 GPU를 효과적으로 활용하기 위해 매우 중요합니다. 공유 온라인 서비스의 경우 지속적인 일괄 처리가 필수적인 반면, 오프라인 일괄 추론 워크로드는 더 간단한 일괄 처리 기술로 높은 처리량을 달성할 수 있습니다.

심층 최적화: 표준 추론 최적화 기법(예: 연산자 결합, 가중치 양자화)도 LLM에 중요하지만, 더 심층적인 시스템 최적화, 특히 메모리 활용도를 개선하는 최적화를 탐색하는 것이 중요합니다. 한 가지 예로 KV 캐시 양자화를 들 수 있습니다.

하드웨어 구성: 모델 유형과 예상 워크로드를 사용하여 배포 하드웨어를 결정해야 합니다. 예를 들어, 여러 개의 GPU로 확장할 때 MPT-7B와 같은 소형 모델의 경우 MBU가 Llama2-70B와 같은 대형 모델보다 훨씬 더 빠르게 감소합니다. 또한 성능은 텐서 병렬 처리 수준이 높을수록 비선형적으로 확장되는 경향이 있습니다. 하지만 트래픽이 많거나 사용자가 짧은 지연 시간을 위해 추가비용을 기꺼이 지불할 의향이 있는 경우에는 높은 수준의 텐서 병렬 처리가 더 작은 모델에 적합할 수 있습니다.

데이터 기반 의사 결정: 이론을 이해하는 것도 중요하지만, 항상 end-to-end 서버 성능을 측정하는 것이 좋습니다. 추론 배포의 성능이 예상보다 나빠질 수 있는 이유는 여러 가지가 있습니다. 소프트웨어의 비효율성으로 인해 MBU가 예기치 않게 낮을 수 있습니다. 또는 클라우드 제공업체 간의 하드웨어 차이로 인해 예상치 못한 결과가 발생할 수도 있습니다(두 클라우드 제공업체의 8xA100 서버 간에 지연 시간이 2배 차이가 나는 것을 관찰했습니다).

데이터브릭스 모델 서빙을 사용하여 LLM 추론을 시작하실 수 있습니다. 자세한 내용은 설명서를 참조하세요.