← 목록으로

PyTorch 모델 양자화, 속도와 메모리를 동시에 잡는 Quanto

DASIS Team

Quanto는 PyTorch 모델의 추론 속도와 메모리 사용량을 동시에 개선하는 양자화 백엔드입니다. Hugging Face Optimum 라이브러리에 통합되어 있으며, 기존 양자화 방식의 단점인 복잡한 보정(calibration) 과정과 파괴적인 모델 구조 변경 문제를 해결합니다. 특히 GPU 환경에서 별도 코드 수정 없이 LLM 추론 성능을 최적화해야 하는 팀에게 유용합니다. 이 글에서는 Quanto의 동작 원리와 기존 방식과의 차이점, 그리고 성능 벤치마크를 통해 도입 효과를 분석합니다.

기존 PyTorch 양자화의 한계

모델 양자화(Quantization)는 float32(32비트 부동소수점)로 표현된 모델 가중치를 int8(8비트 정수) 등 더 적은 비트로 표현하여 모델 크기를 줄이고 계산 속도를 높이는 기술입니다. PyTorch는 torch.ao.quantization이라는 내장 양자화 툴킷을 제공하지만, 프로덕션 환경에 적용하기에는 몇 가지 제약이 따릅니다.

가장 큰 한계는 정적 양자화(static quantization) 방식이 기본이라는 점입니다. 이 방식은 모델에 입력될 데이터의 분포를 미리 파악하기 위해 보정 데이터셋을 요구합니다. 보정 과정은 번거로울 뿐만 아니라, 실제 추론 시 입력 데이터 분포가 보정 데이터와 다르면 성능이 저하됩니다. 또한, 양자화 과정이 모델의 state_dict를 직접 수정하여 영구적으로 변경합니다. 이는 양자화된 모델을 다시 원래의 float32 상태로 되돌리기 어렵게 만들어 디버깅과 실험을 까다롭게 합니다.

Quanto의 핵심, QuantizedTensor

Quanto는 PyTorch 2.0에 도입된 Tensor Subclassing 기능을 활용해 이 문제를 해결합니다. QuantizedTensor라는 새로운 텐서 서브클래스를 정의하여, 양자화된 데이터와 스케일(scale) 값을 하나의 객체로 관리합니다. QuantizedTensortorch.Tensor를 상속받기 때문에 일반 텐서와 거의 동일하게 동작합니다. 따라서 기존 PyTorch 연산자와 호환되며, 모델 코드를 거의 수정하지 않고도 양자화 적용이 가능합니다.

QuantizedTensor는 내부적으로 양자화된 데이터(_data), 스케일(_scale), 양자화 타입(_qtype)을 속성으로 가집니다. 스케일은 원래의 float32 값과 양자화된 정수 값 사이의 변환 비율을 저장하는 핵심 파라미터입니다.

quantized_value=round(xscale)quantized\_value = \text{round}\left(\frac{x}{scale}\right) dequantized_value=quantized_value×scaledequantized\_value = quantized\_value \times scale

이 구조 덕분에 연산은 양자화된 데이터로 수행하고, 필요할 때만 스케일을 이용해 원래 값으로 복원할 수 있습니다. 모델 가중치 자체는 float32로 유지하면서 순전파(forward pass) 과정에서 실시간으로 양자화 연산을 수행하기 때문에, 모델의 state_dict가 변경되지 않습니다.

모델 양자화 흐름: quantize부터 freeze까지

Quanto를 이용한 모델 양자화는 quantizefreeze 두 단계로 이루어집니다.

먼저 quantize 함수는 PyTorch 모델(nn.Module)을 순회하며 양자화가 가능한 레이어(예: nn.Linear, nn.Conv2d)를 찾아 QuantizedModule로 교체합니다. 예를 들어 nn.Linearqlinear로 바뀝니다. 이 단계에서는 모델 가중치를 실제로 양자화하지 않습니다. 가중치는 여전히 float32로 남아있고, 순전파 시에만 동적으로 양자화와 역양자화가 일어납니다.

import torch
from quanto import quantize, freeze

# 간단한 모델 정의
model = torch.nn.Sequential(torch.nn.Linear(32, 64))

# 모델의 nn.Linear 레이어를 QuantizedLinear로 교체한다
quantize(model, weights=torch.int8, activations=None)
print(type(model[0]))
# <class 'quanto.nn.qlinear.QLinear'>

이 상태의 모델은 디버깅과 유연성 측면에서 유리합니다. 하지만 매번 연산 시 스케일을 동적으로 계산해야 하므로 추론 성능 최적화에는 한계가 있습니다.

최적의 추론 성능을 위해서는 freeze 함수를 호출합니다. freeze는 모델의 가중치를 실제로 int8로 변환하고, 동적으로 계산되던 스케일 값을 상수로 고정합니다. 이 과정을 통해 양자화된 가중치와 고정된 스케일을 사용하는 전용 CUDA 커널을 활용하여 행렬 곱셈 연산을 가속할 수 있습니다.

# 모델 가중치를 int8로 변환하고 스케일을 고정한다
freeze(model)

# freeze 이후에는 가중치 텐서가 QuantizedTensor로 변환된다
print(type(model[0].weight))
# <class 'quanto.tensor.qtensor.QuantizedTensor'>

freeze를 호출하고 나면 모델 가중치는 영구적으로 양자화된 상태가 되므로, 더 이상 양자화 이전 상태로 되돌릴 수 없습니다. 따라서 개발 단계에서는 quantize만 사용해 기능을 검증하고, 배포 시점에 freeze를 적용하여 성능을 극대화하는 것이 일반적인 워크플로우입니다.

성능 벤치마크: 메모리, 속도, 정확도

공개된 벤치마크 결과는 Quanto의 효과를 보여줍니다. Llama-2-7B-chat-hf 모델을 NVIDIA L4 GPU(24GB VRAM)에서 테스트했을 때, float16과 int8 양자화 사이의 성능 차이는 명확합니다.

Metricfloat16 (Baseline)quanto int8변동률
Latency (토큰당 ms)16.489.94-39.7%
Throughput (초당 토큰)60.67100.59+65.8%
Peak Memory (GB)14.58.2-43.4%
MMLU Score (Accuracy)45.445.3-0.1
Llama-2-7B-chat-hf 모델 벤치마크 기준

결과는 인상적입니다. int8 양자화를 적용하자 추론 지연 시간(latency)이 약 40% 감소했고, GPU 최고 사용 메모리(Peak Memory)는 43% 이상 줄었습니다. 모델 크기가 줄어들면서 동일한 시간 동안 더 많은 토큰을 처리할 수 있게 되어 처리량(Throughput)은 65% 넘게 증가했습니다. 이 모든 성능 향상이 MMLU 점수 기준 0.1점 하락이라는 미미한 정확도 손실만으로 이루어졌습니다.

트레이드오프: 언제 Quanto가 최선이 아닌가

Quanto는 사용 편의성과 성능 사이의 균형을 제공하지만, 모든 상황에 맞는 해결책은 아닙니다. 미세한 정확도 하락도 허용할 수 없는 금융 예측이나 의료 진단 모델에는 부적합할 수 있습니다. 벤치마크에서 0.1점의 손실은 사소해 보이지만, 특정 태스크에서는 이 차이가 치명적인 오류로 이어질 수 있습니다.

또한, Quanto는 가중치(weight) 양자화에 주로 초점을 맞춥니다. 활성화(activation) 값까지 양자화하면 성능을 더 높일 수 있지만, 그만큼 정확도가 떨어질 위험도 커집니다. 따라서 활성화 양자화는 모델과 태스크의 특성을 고려해 신중하게 적용해야 합니다.

정적 양자화 방식과 비교할 때, 동적 양자화는 입력 데이터에 따라 실시간으로 스케일을 계산하는 오버헤드가 존재합니다. freeze를 통해 이 오버헤드를 줄일 수 있지만, 입력 데이터 패턴이 극도로 일정하고 예측 가능한 환경이라면, 사전에 보정 작업을 거친 정적 양자화가 미세하게 더 나은 성능을 보일 수도 있습니다.

이런 팀에 Quanto를 추천합니다

Quanto는 기존 PyTorch 양자화 도구의 복잡성과 모델 구조를 영구적으로 변경하는 문제에 부담을 느끼는 팀에게 현실적인 대안이 됩니다. QuantizedTensor를 활용한 비파괴적 양자화, quantizefreeze로 이어지는 직관적인 워크플로우는 개발 편의성과 추론 성능을 모두 만족시킵니다.

특히 GPU에서 LLM 추론 서비스를 운영하며, 빠른 프로토타이핑과 운영 안정성을 모두 잡아야 하는 상황이라면 Quanto 도입을 검토할 가치가 충분합니다. 모델의 state_dict를 변경하지 않으므로 기존 배포 파이프라인과 호환성을 유지하기 쉽고, 상당한 수준의 메모리 절감과 속도 향상을 최소한의 정확도 손실로 얻을 수 있기 때문입니다.