KV Cache
Key-Value 캐시, 트랜스포머 추론 속도를 높이는 기법
Key-Value 캐시, 트랜스포머 추론 속도를 높이는 기법
KV Cache(Key-Value Cache)는 트랜스포머 모델의 추론 속도를 획기적으로 개선하는 기법입니다. Attention 계산에서 이전 토큰들의 Key와 Value 텐서를 저장해두고 재사용함으로써, 각 새 토큰 생성 시 전체 시퀀스를 다시 계산하는 중복 연산을 제거합니다.
LLM이 텍스트를 생성할 때는 autoregressive 방식으로 한 토큰씩 출력합니다. KV Cache가 없으면 N번째 토큰 생성 시 1~N-1번째 토큰의 K,V를 매번 재계산해야 하므로 O(N^2) 복잡도가 됩니다. KV Cache로 이를 O(N)으로 줄일 수 있습니다.
2023년 vLLM의 PagedAttention은 KV Cache 메모리 관리를 혁신했습니다. GPU 메모리를 페이지 단위로 동적 할당해 메모리 단편화를 4% 미만으로 줄였고(기존 70%), 같은 GPU로 2-4배 더 많은 요청을 처리할 수 있게 되었습니다.
2025년 기준 128K 토큰 컨텍스트가 표준이 되면서 KV Cache 최적화가 더욱 중요해졌습니다. LMCache는 GPU HBM, CPU RAM, SSD의 계층적 저장을 활용해 15배 throughput 향상을 달성했고, llm-d의 KV Cache Aware Routing은 87% 캐시 히트율로 TTFT를 88% 단축했습니다.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
import time
# 모델 로드
model_name = "meta-llama/Llama-2-7b-chat-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
prompt = "인공지능의 미래에 대해 설명해주세요:"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
# KV Cache 비활성화 (비교용 - 매우 느림)
print("=== KV Cache 비활성화 ===")
start = time.time()
with torch.no_grad():
output_no_cache = model.generate(
**inputs,
max_new_tokens=100,
use_cache=False, # KV Cache 비활성화
do_sample=False
)
time_no_cache = time.time() - start
print(f"생성 시간: {time_no_cache:.2f}초")
# KV Cache 활성화 (기본값 - 빠름)
print("\n=== KV Cache 활성화 ===")
start = time.time()
with torch.no_grad():
output_with_cache = model.generate(
**inputs,
max_new_tokens=100,
use_cache=True, # KV Cache 활성화 (기본값)
do_sample=False
)
time_with_cache = time.time() - start
print(f"생성 시간: {time_with_cache:.2f}초")
speedup = time_no_cache / time_with_cache
print(f"\n속도 향상: {speedup:.1f}배")
# KV Cache 직접 관리 (streaming 용도)
past_key_values = None
generated_tokens = []
for _ in range(50):
with torch.no_grad():
outputs = model(
**inputs if past_key_values is None else {"input_ids": next_token_id},
past_key_values=past_key_values,
use_cache=True
)
past_key_values = outputs.past_key_values # KV Cache 저장
next_token_id = outputs.logits[:, -1, :].argmax(dim=-1, keepdim=True)
generated_tokens.append(next_token_id.item())
# KV Cache 크기 확인 (첫 레이어 기준)
if len(generated_tokens) == 1:
kv_shape = past_key_values[0][0].shape
print(f"KV Cache shape: {kv_shape} (layer, batch, heads, seq, dim)")
"현재 LLM 서비스가 GPU 메모리 부족으로 동시 요청 처리가 제한됩니다. vLLM의 PagedAttention으로 전환하면 KV Cache 메모리 효율이 70%에서 96%로 개선되어 같은 GPU로 3배 더 많은 요청을 처리할 수 있습니다."
"KV Cache는 이전 토큰들의 Key, Value를 저장해서 재사용하는 겁니다. 없으면 N번째 토큰 생성 시 1~N-1번 토큰을 모두 재계산해야 해서 O(N^2)이 되는데, KV Cache로 O(N)으로 줄일 수 있어요. 단점은 메모리 사용량 증가입니다."
"128K 컨텍스트에서 KV Cache가 수십 GB까지 커집니다. LMCache처럼 CPU RAM이나 SSD로 오프로드하거나, Entropy-Guided Caching으로 중요도 낮은 레이어의 캐시를 줄이는 방법이 있어요."
32K 토큰 이상에서 KV Cache만 10GB 이상 차지할 수 있습니다. max_length 제한이나 캐시 압축을 적용하세요.
패딩 토큰까지 KV Cache에 저장하면 메모리 낭비입니다. attention_mask를 올바르게 설정하세요.
프로덕션 환경에서는 PagedAttention이 구현된 추론 엔진을 사용해 메모리 효율과 throughput을 극대화하세요.