KV 캐시에 INT8 양자화를 걸고 품질을 모니터링하다 보면 이상한 패턴과 마주친다. Value 쪽은 생각보다 손실이 적은데, Key 쪽에서만 특정 레이어의 Attention Score가 뭉개진다. 같은 8비트를 썼는데 왜 이런 비대칭이 생기는 걸까.
답은 Attention 연산 구조에 있다. Key와 Value는 텐서 형태는 비슷해 보여도 연산 경로가 근본적으로 다르고, 그 차이가 양자화 오차의 전파 방식을 갈라놓는다.
Key와 Value의 연산 경로
Attention 출력을 수식으로 쓰면 이렇다:
Score = softmax(QK^T / sqrt(d_k))
Output = Score · V
Key의 양자화 오차는 QK^T 내적에서 Score를 만드는 단계에서 먼저 개입한다. 문제는 그 다음 softmax다. softmax는 비선형 함수여서 Score 값이 조금만 흔들려도 확률 분포 전체가 뒤틀린다. 두 토큰의 raw score 차이가 0.5에서 0.9로 바뀌면, softmax를 통과한 attention weight 비율은 훨씬 크게 달라진다. 이 증폭이 Key 양자화 오차를 위험하게 만든다.
Value의 양자화 오차는 경로가 다르다. Score · V는 단순 선형 가중합이다. 이미 softmax를 지나 정규화된 Score가 V를 가중 평균하는 과정이라서, V에 작은 오차가 있어도 전파 방식은 선형적이고 예측 가능하다. Score 분포가 훼손되지 않은 상태에서 V가 조금 틀리면 출력도 그 비율만큼 틀린다. 비선형 증폭이 없다.
KIVI (Liu et al., ICML 2024)가 이 비대칭을 실증적으로 확인했다. Key 캐시는 per-channel 양자화를, Value 캐시는 per-token 양자화를 각각 적용하는 비대칭 전략으로 2비트까지 압축하면서 Llama, Falcon, Mistral 모델에서 품질을 거의 유지했다. 피크 메모리는 2.6× 감소, 처리량은 2.35~3.47× 개선이었다.
Key 채널의 이상치 분포
per-tensor INT8 양자화가 Key에서 특히 망가지는 이유는 채널별 분산 비대칭 때문이다.
Key 텐서를 채널 차원으로 슬라이스해 보면, 특정 채널 몇 개의 값 분포가 나머지 채널과 비교해 10배 이상 크게 튀는 경우가 흔하다. Llama 계열 모델에서 KV 캐시를 분석하면 소수의 채널이 전체 분산의 상당 비중을 차지하는 패턴이 반복적으로 나온다. per-tensor 양자화는 텐서 전체에 scale 값 하나를 쓰기 때문에, 그 scale은 이상치 채널의 범위를 커버하도록 결정된다. 결과적으로 이상치가 없는 일반 채널들은 사용 가능한 INT8 표현 범위의 극히 일부만 점유하게 되고, 대부분의 채널에서 양자화 해상도가 낭비된다.
Value 텐서는 이 문제가 덜 심하다. 채널 간 분산이 상대적으로 고르기 때문에 per-tensor scale 하나로도 전체 텐서를 비교적 균등하게 커버한다. KVQuant (Hooper et al., NeurIPS 2024)는 "Key 행렬은 평균 크기가 다른 채널보다 큰 뚜렷한 이상치 채널이 존재한다"고 명시하고, per-channel Key 양자화가 이 이상치 채널의 영향이 다른 채널로 번지지 않게 막는 설계라고 설명한다.
per-channel 양자화는 채널별로 독립적인 scale/zero-point를 가져서, 이상치 채널이 튀어도 그 scale은 해당 채널에만 적용된다. 이상치가 없는 채널은 자기 분포에 맞는 scale을 따로 가지므로 INT8 표현 범위를 온전히 활용한다.
INT8 per-tensor / per-channel / FP8 e4m3: 실측 비교
KVQuant가 3-bit 양자화로 LLaMA-7B를 Wikitext-2에서 평가한 결과를 보면 방식별 격차가 뚜렷하다:
| 양자화 방식 | Wikitext-2 PPL 변화 |
|---|---|
| BF16 기준선 | 0 |
| Key per-channel + Value per-token (KVQuant) | < 0.1 증가 |
| Post-RoPE 대비 Pre-RoPE Key 양자화 적용 | 0.82 PPL 향상 (3-bit LLaMA-7B) |
Key만 양자화했을 때와 Value만 양자화했을 때를 비교하는 ablation에서도 Value 쪽 손실이 일관되게 작다. GSM8k 수학 추론 태스크에서 이 격차가 특히 크게 벌어진다. 추론 체인이 길수록 Key 이상치 채널이 뭉개질 때의 영향이 누적되기 때문이다.
FP8은 다른 방향에서 이 문제에 접근한다. e4m3는 4-bit exponent, 3-bit mantissa 구조로 표현 범위가 ±240으로 좁은 대신 mantissa 해상도가 높아서 작은 값의 구분 정밀도가 INT8 per-tensor보다 낫다. 채널별 calibration scale과 함께 쓰면 이상치 채널을 per-channel INT8과 유사한 수준으로 처리한다. e5m2는 exponent를 5비트에 배정해서 범위는 넓지만 정밀도는 떨어진다. Key 이상치 채널의 값 범위가 매우 넓다면 e5m2가 유리하고, 범위가 좁고 정밀도가 우선이라면 e4m3가 낫다.
레이어 깊이와 민감도
KV 양자화 오차 허용도는 레이어마다 다르다.
초반 레이어의 Attention은 토큰 위치와 문법 구조 같은 지역적 패턴을 처리한다. 이 레이어의 Key 이상치 채널이 뭉개지면 모델이 기본적인 시퀀스 구조를 놓치게 되고, 그 오류가 이후 레이어 전체로 전파된다. 깊은 레이어에서는 이미 오염된 표현을 가지고 의미를 복원해야 하는데, 그게 가능한 경우는 드물다. 반대로 깊은 레이어는 초반 레이어의 표현이 건강한 상태에서 들어오면 양자화 오차에 비교적 관대하다.
이 구조적 이유로 모든 레이어에 동일한 양자화 방식을 적용하는 전략은 효율적이지 않다. KVTuner (2025) 같은 후속 연구들이 레이어별 민감도를 측정해 비트 수를 달리 배정하는 mixed precision KV 방향으로 발전하고 있는 것도 이 이유에서다.
vLLM은 --kv-cache-dtype-skip-layers 플래그로 특정 레이어 인덱스나 레이어 타입을 FP8에서 제외할 수 있게 한다:
# 초반 레이어와 특정 인덱스를 BF16으로 유지
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--kv-cache-dtype fp8 \
--kv-cache-dtype-skip-layers 0 1 23
# 슬라이딩 윈도우 레이어 타입 전체 제외
vllm serve <model> --kv-cache-dtype fp8 \
--kv-cache-dtype-skip-layers sliding_window
sliding-window attention을 쓰는 하이브리드 모델에서 이 옵션이 특히 효과적이다. 슬라이딩 윈도우 레이어는 KV 길이가 짧아서 양자화로 얻는 메모리 이득이 적은 반면, 품질 손실에는 민감하기 때문이다.
실무 선택
현재 주요 서빙 프레임워크는 Key와 Value를 분리해서 다른 양자화 방식을 적용하는 기능을 공식 인터페이스로 노출하지 않는다. vLLM과 SGLang 모두 --kv-cache-dtype은 Key와 Value에 동일한 dtype을 적용한다. KIVI나 KVQuant가 논문에서 증명한 비대칭 전략을 그대로 쓰려면 별도 패치나 커스텀 Attention 커널이 필요하다.
현실적인 선택지는 이 세 가지다:
| 옵션 | KV 메모리 절감 | 정확도 | 설정 복잡도 |
|---|---|---|---|
--kv-cache-dtype auto (BF16) | 없음 | 기준선 | 없음 |
--kv-cache-dtype fp8_e4m3 | ~50% | 대부분 태스크에서 무시 가능 | 낮음 |
--kv-cache-dtype fp8 + 레이어 스킵 | 스킵 레이어 수에 따라 감소 | 단문 분류는 무시 가능, 긴 문서 요약은 확인 필요 | 중간 |
태스크가 긴 컨텍스트 요약이나 복잡한 추론이라면 fp8_e4m3 + 초반 레이어 스킵을 먼저 시험하고 task-specific metric을 측정하는 게 안전하다. 짧은 분류나 단순 Q&A라면 기본 fp8으로도 실 서비스에서 품질 차이를 체감하기 어렵다.
calibration 없이 scale=1.0으로 쓰는 기본값보다는 llm-compressor로 데이터셋 기반 calibration scale을 구하는 편이 Key 채널 이상치가 큰 모델에서 정확도를 지킨다. vLLM 공식 문서는 이 calibration 경로를 권장 경로로 명시한다.
Key/Value 분리 설정을 프레임워크 수준에서 공식 지원하는 시점이 언제가 될지는 아직 모른다. KIVI 구현체가 공개되어 있고 KVQuant도 코드가 있지만, vLLM 메인라인에 Key per-channel + Value per-token을 동시에 쓰는 공식 옵션은 현재 없다. 긴 컨텍스트 서빙 수요가 늘수록 이 방향 구현 압력이 커질 것이다.