Skip to main content
Summary

실서비스의 병목은 모델 품질만이 아니다

Gemma를 처음 실행할 때는 "답이 그럴듯한가"에 집중한다. 그러나 서비스에 올리는 순간 질문이 바뀐다. 사용자가 첫 단어를 보기까지 몇 초가 걸리는가. 동시에 몇 명을 처리할 수 있는가. GPU 한 장으로 하루에 몇 건을 감당할 수 있는가. 같은 품질을 더 낮은 비용으로 낼 수 있는가.

추론 최적화는 이 질문에 답하는 과정이다. 여기서 추론은 학습이 끝난 Gemma가 입력을 읽고 답변을 생성하는 실행 단계를 뜻한다. 실무에서는 다음 지표를 함께 본다.

  • TTFT: Time To First Token. 요청 후 첫 토큰이 나오기까지의 시간이다.

  • tokens/sec: 초당 생성 토큰 수다. 답변 전체 속도와 관련이 깊다.

  • p95·p99 지연 시간: 전체 요청 중 느린 5%, 1% 요청의 응답 시간이다.

  • GPU 메모리 사용량: 모델 가중치, KV 캐시, 실행 오버헤드를 모두 포함한다.

  • 처리량: 일정 시간 동안 처리한 요청 수 또는 토큰 수다.

평균 응답 시간이 좋아도 p99가 나쁘면 일부 사용자는 서비스가 멈췄다고 느낀다. 처리량이 좋아도 첫 토큰이 늦으면 채팅 UI에서는 답답하다. 따라서 최적화의 목표는 하나가 아니다. 품질, 첫 응답 속도, 전체 응답 시간, 동시 처리량, 비용의 균형을 잡아야 한다.

속도와 비용을 줄이는 추론 최적화AI 생성 이미지

먼저 FP16/BF16 기준선을 만든다

양자화나 캐시 최적화를 바로 적용하기 전에 기준 실행을 만들어야 한다. 보통 GPU에서는 FP16 또는 BF16으로 시작한다. FP16은 16비트 부동소수점이고, BF16은 bfloat16이라는 형식이다. 둘 다 FP32보다 메모리를 적게 쓰며, 최신 GPU에서 LLM 추론에 널리 쓰인다.

기준선에서는 다음을 기록한다.

  • 모델 이름과 버전

  • 입력 토큰 수와 출력 토큰 수

  • temperature, top-p, max tokens 같은 생성 설정

  • TTFT, 전체 응답 시간, tokens/sec

  • 최대 동시 요청 수

  • GPU 메모리 사용량

  • 평가 데이터셋의 정확도, 형식 준수율, 거절 정확도

이 기준선이 있어야 INT8이나 INT4를 적용했을 때 "빨라졌다", "싸졌다", "품질이 떨어졌다"를 판단할 수 있다. 데모 질문 몇 개로 판단하면 위험하다. 특히 사내 문서 Q&A, 법무 검토, 의료·금융 보조처럼 도메인 용어가 중요한 서비스에서는 작은 품질 저하가 실제 장애가 될 수 있다.

가중치 양자화: 메모리는 줄지만 품질 검증이 필요하다

양자화는 모델 가중치나 연산을 더 낮은 비트 수로 표현하는 기법이다. 단순히 말하면 모델의 숫자를 더 작은 형식으로 저장해 메모리와 비용을 줄이는 방법이다.

예를 들어 모델 가중치만 거칠게 보면 BF16은 파라미터 하나에 2바이트를 쓴다. 9B 모델이라면 가중치만 약 18GB가 필요하다. INT8은 대략 절반, INT4는 다시 절반 수준까지 줄일 수 있다. 실제 서비스에서는 스케일 값, 런타임 오버헤드, KV 캐시가 더해지므로 정확히 이 숫자로 끝나지는 않지만, 방향은 분명하다. 낮은 비트로 갈수록 더 작은 GPU에서 실행하거나 더 많은 요청을 처리할 가능성이 생긴다.

다만 비용 절감에는 대가가 있다.

  • INT8: 품질 저하가 비교적 작고, 운영 적용 후보로 검토하기 좋다.

  • INT4: 메모리 절감 효과가 크지만, 답변 품질·계산 정확도·긴 문맥 처리·JSON 형식 준수에서 회귀가 생길 수 있다.

  • 도메인 작업: 숫자, 코드, 정책 조항, 계약 문구처럼 정밀한 출력이 필요한 작업은 더 엄격히 테스트해야 한다.

INT4 모델이 일반 대화에서는 괜찮아 보여도, 실제 업무 질문에서는 문서 조항을 잘못 인용하거나 출력 형식을 깨뜨릴 수 있다. 따라서 양자화 적용 전후로 같은 골든 데이터셋을 돌려야 한다. 질문 100~500개 정도라도 좋다. 중요한 것은 "좋아 보이는 예시"가 아니라 반복 가능한 비교다.

실무 판단은 단순하다. INT4로 GPU 비용이 40% 줄어도 핵심 답변 정확도가 5% 떨어져 고객 지원 티켓이 늘어난다면 손해다. 반대로 문장 교정, 짧은 요약, 초안 생성처럼 약간의 표현 차이를 허용하는 작업이라면 INT4가 좋은 선택일 수 있다.

KV 캐시: 긴 컨텍스트와 동시성의 숨은 비용

Gemma가 답변을 생성할 때는 이전 토큰의 계산 결과를 계속 참조한다. 이때 저장되는 key와 value 값이 KV 캐시다. KV 캐시는 다음 토큰을 빠르게 생성하게 해 주지만, 메모리를 많이 사용한다.

KV 캐시 메모리는 대체로 다음 요소에 비례해 커진다.

  • 동시 처리 중인 요청 수

  • 각 요청의 입력 토큰 수

  • 각 요청이 생성 중인 출력 토큰 수

  • 모델의 레이어 수와 attention 구조

  • KV 캐시 데이터 타입

긴 RAG 컨텍스트를 넣고, 사용자도 많고, 답변도 길게 생성하면 병목은 모델 가중치가 아니라 KV 캐시가 된다. "GPU 메모리가 남아 보이는데 동시 요청이 늘면 갑자기 OOM이 난다"는 상황이 여기서 자주 생긴다.

이 문제를 줄이는 방법 중 하나가 KV 캐시 양자화다. vLLM은 FP8 KV 캐시를 지원하며, KV 캐시의 메모리 사용량을 줄여 더 많은 토큰을 메모리에 저장하고 긴 컨텍스트나 더 많은 동시 요청을 처리하는 데 도움을 줄 수 있다.1 기본값처럼 모델 dtype을 그대로 쓰는 대신 kv_cache_dtype을 FP8로 설정하는 방식이다. CUDA와 ROCm 환경에서 지원되는 FP8 형식이 있으며, Flash Attention 3과 함께 사용할 때는 attention 연산 경로도 FP8 기반으로 동작할 수 있다.1

다만 KV 캐시 양자화도 무조건 켜는 기능이 아니다. 품질과 지연 시간, 처리량을 같이 측정해야 한다. vLLM 문서에서는 스케일을 기본값으로 두는 방식, 워밍업 중 랜덤 토큰으로 추정하는 방식, llm-compressor를 이용해 데이터셋으로 보정하는 방식을 설명한다. 가장 안정적인 검증은 실제 서비스 입력과 비슷한 데이터로 보정하고 평가하는 것이다.1

운영에서 기억할 원칙은 간단하다.

  • 컨텍스트가 짧고 동시 요청이 적으면 KV 캐시 양자화 효과가 작을 수 있다.

  • 긴 문서 RAG와 고동시성 서버에서는 KV 캐시가 핵심 병목이 될 가능성이 크다.

  • max_model_len을 실제보다 과하게 잡으면 KV 캐시 여유 공간이 줄어든다.

  • FP8 KV 캐시를 쓰더라도 도메인 평가와 부하 테스트를 거쳐야 한다.

프리필과 디코딩을 나눠서 본다

LLM 추론은 크게 두 단계로 볼 수 있다.

첫째, 프리필(prefill) 단계다. 모델이 입력 프롬프트 전체를 읽고 내부 상태를 계산한다. RAG에서 검색 문서 10개를 통째로 넣거나, 긴 대화 이력을 모두 붙이면 이 단계가 무거워진다.

둘째, 디코딩(decode) 단계다. 모델이 답변 토큰을 하나씩 생성한다. 출력이 길수록 이 단계가 오래 걸린다.

이 둘은 병목이 다르다. 입력이 긴 서비스는 프리필 비용이 크다. 답변을 길게 쓰는 서비스는 디코딩 비용이 크다. 최적화도 다르게 해야 한다.

  • 프리필이 느리면 입력 토큰을 줄이고, RAG 결과를 압축하고, 중복 문서를 제거한다.

  • 디코딩이 느리면 최대 출력 길이를 제한하고, 불필요한 장문 답변을 막는다.

  • 첫 토큰이 늦으면 스트리밍과 스케줄링 최적화를 검토한다.

  • 처리량이 낮으면 배치 처리와 런타임 교체를 검토한다.

스트리밍은 총 계산량을 줄이지는 않는다. 하지만 첫 토큰을 빨리 보여 주기 때문에 사용자의 체감 속도를 크게 개선한다. 사용자는 전체 답변이 끝날 때까지 빈 화면을 보는 것보다, 답변이 생성되는 과정을 보는 쪽을 더 빠르게 느낀다.

연속 배치 처리로 GPU 유휴 시간을 줄인다

일반적인 배치 처리는 여러 요청을 모아 한 번에 처리한다. 문제는 LLM 요청의 길이가 제각각이라는 점이다. 어떤 사용자는 짧은 질문을 하고, 어떤 사용자는 긴 문서를 붙인다. 어떤 답변은 50토큰에서 끝나고, 어떤 답변은 1000토큰까지 이어진다. 고정 배치 방식에서는 짧은 요청이 긴 요청을 기다리며 GPU가 비효율적으로 쓰일 수 있다.

연속 배치 처리(continuous batching)는 이 문제를 줄인다. 실행 중인 배치에서 끝난 요청은 빼고, 새 요청을 계속 넣는다. 덕분에 GPU가 쉬는 시간을 줄이고 전체 처리량을 높일 수 있다. vLLM 같은 추론 서버가 고동시성 배포에서 자주 선택되는 이유도 여기에 있다.

또 하나의 전략은 스케줄링을 비동기로 처리하는 것이다. vLLM의 Gemma 4 사용 가이드는 --async-scheduling 같은 옵션을 통해 스케줄링과 디코딩을 겹쳐 전체 처리량을 높이는 방향을 제시한다.2 이런 옵션은 단일 요청의 속도보다 여러 요청이 몰릴 때의 효율에 더 큰 영향을 줄 수 있다.

런타임은 목적에 맞게 고른다

Gemma를 실행하는 방법은 하나가 아니다. 같은 모델이라도 런타임에 따라 설치 난이도, 속도, 동시 처리량, 양자화 지원, 운영 편의성이 달라진다.

  • Ollama: 로컬 실험과 작은 서버 배포가 쉽다. 빠르게 프로토타입을 만들기에 좋다.

  • llama.cpp: CPU, Apple Silicon, 엣지 환경, GGUF 양자화 모델 실행에 강하다.

  • TGI(Text Generation Inference): Hugging Face 생태계와 잘 맞고, 서버형 추론에 적합하다.

  • vLLM: 고동시성, 연속 배치 처리, OpenAI 호환 API, 긴 컨텍스트 서버 배포에 강하다.

운영 서버에서 동시 사용자가 많고 GPU를 효율적으로 쓰고 싶다면 vLLM을 우선 검토할 만하다. vLLM의 Gemma 4 가이드는 dense 모델과 MoE 모델, 멀티모달 입력, 구조화 출력, 도구 호출 같은 기능 지원 범위를 설명하며, --max-model-len, --gpu-memory-utilization, tensor parallel 설정을 배포 상황에 맞게 조정하도록 안내한다.2

예를 들어 --max-model-len은 모델이 받을 수 있는 최대 컨텍스트 길이다. 무조건 크게 잡으면 좋아 보이지만, 실제로는 KV 캐시 메모리를 더 많이 예약하게 되어 동시 처리량이 줄 수 있다. 사내 Q&A의 평균 입력이 3000토큰이고 긴 요청도 12000토큰 안에서 해결된다면, 처음부터 131072 같은 최대값을 열어 둘 필요가 없다. 긴 컨텍스트는 기능이 아니라 비용이다.

가장 싼 토큰은 보내지 않은 토큰이다

추론 최적화에서 가장 효과적인 방법은 런타임 옵션보다 입력을 줄이는 것이다. 모델에 들어가는 토큰이 줄면 프리필 시간이 줄고, KV 캐시 사용량도 줄며, 비용도 내려간다.

RAG 시스템에서는 특히 다음을 점검해야 한다.

  • 검색 결과 top-k가 과도하게 크지 않은가

  • 같은 내용을 담은 청크가 중복으로 들어가지 않는가

  • 문서 제목, 경로, HTML 메뉴, 푸터 같은 잡음이 포함되지 않았는가

  • 질문과 관련 없는 배경 설명까지 넣고 있지 않은가

  • 긴 문서를 요약·압축해 넣어도 답변 품질이 유지되는가

  • 시스템 프롬프트가 지나치게 장황하지 않은가

  • 이전 대화 전체를 매번 넣고 있지 않은가

긴 컨텍스트 모델을 쓰면 많은 문서를 넣을 수 있다. 하지만 넣을 수 있다는 것과 넣어야 한다는 것은 다르다. 관련 없는 근거가 많아지면 비용만 늘어나는 것이 아니라 답변 품질도 흔들릴 수 있다. 모델이 중요하지 않은 문장을 근거로 착각하거나, 서로 충돌하는 문서 사이에서 잘못된 결론을 낼 수 있다.

대화형 서비스에서는 전체 대화 이력을 계속 붙이는 대신, 필요한 상태만 요약해 넣는 편이 좋다. 예를 들어 사용자의 선호, 현재 작업 목표, 최근 결정 사항만 유지하고 농담이나 잡담은 버린다. 이렇게 하면 속도와 비용을 줄이면서도 대화의 연속성을 유지할 수 있다.

GPU 수와 요청 제한을 계산한다

운영 용량 산정은 감으로 하면 안 된다. 최소한 다음 값을 정해야 한다.

  • 예상 동시 사용자 수

  • 실제로 동시에 생성 중인 요청 수

  • 평균 입력 토큰 수

  • 평균 출력 토큰 수

  • p95 입력·출력 토큰 수

  • 목표 TTFT

  • 목표 p95 전체 응답 시간

  • 모델 가중치 메모리

  • 요청당 KV 캐시 메모리

  • GPU 메모리 여유분

거칠게는 다음 구조로 생각할 수 있다.

필요 GPU 메모리 ≈ 모델 가중치 + 동시 요청의 KV 캐시 + 런타임 오버헤드

여기서 많은 팀이 실수하는 부분은 "동시 사용자 수"와 "동시 생성 요청 수"를 구분하지 않는 것이다. 사용자가 1000명 접속해 있어도 동시에 답변을 생성 중인 요청은 30개일 수 있다. 반대로 사용자는 100명뿐이어도 모두 긴 문서를 넣고 긴 답변을 요구하면 GPU 한 장으로 버티기 어렵다.

운영 정책도 함께 정해야 한다.

  • 최대 입력 토큰 수 제한

  • 최대 출력 토큰 수 제한

  • 사용자별 분당 요청 수 제한

  • 조직별 동시 요청 수 제한

  • 긴 요청 전용 큐 분리

  • 과부하 시 429 응답 또는 짧은 모델로 폴백

  • RAG 검색 결과 개수 제한

이 제한은 사용자를 괴롭히기 위한 장치가 아니다. 전체 서비스를 안정적으로 유지하기 위한 안전판이다. 제한이 없으면 일부 긴 요청이 GPU 메모리를 점유해 다른 사용자의 정상 요청까지 느려진다.

최적화는 한 번에 하나씩 적용한다

추론 최적화에서 가장 나쁜 방식은 여러 변경을 한꺼번에 적용하는 것이다. INT4로 바꾸고, KV 캐시를 FP8로 바꾸고, 런타임도 교체하고, 프롬프트도 줄이면 무엇 때문에 품질이 바뀌었는지 알 수 없다.

추천 순서는 다음과 같다.

  1. FP16 또는 BF16 기준선을 만든다.

  2. 입력·출력 토큰 수를 줄인다.

  3. max_model_len과 출력 제한을 실제 워크로드에 맞춘다.

  4. 배치 처리와 스트리밍을 적용한다.

  5. INT8 가중치 양자화를 평가한다.

  6. 필요하면 INT4를 별도 평가한다.

  7. 긴 컨텍스트·고동시성 병목이 있으면 FP8 KV 캐시를 검토한다.

  8. 부하 테스트로 p95·p99 지연 시간과 장애 패턴을 확인한다.

Gemma 서비스의 목표는 가장 큰 모델을 가장 화려하게 실행하는 것이 아니다. 사용자가 충분히 신뢰할 수 있는 답을, 정해진 시간 안에, 감당 가능한 비용으로 제공하는 것이다. 빠른 서비스는 단순히 GPU가 좋은 서비스가 아니다. 불필요한 토큰을 줄이고, 캐시 메모리를 관리하며, 배치를 효율적으로 구성하고, 품질 회귀를 꾸준히 확인하는 서비스다.

참고

1vLLM Quantized KV Cache

2vLLM Gemma 4 Usage Guide

책 목록

Gemma 실전 사용 가이드: Google 공개 모델로 LLM 개발부터 배포까지

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기