GPU 서버 비용은 ML 인프라 운영에서 가장 큰 지출 항목 중 하나입니다. A100 80GB 온디맨드 인스턴스 하나가 시간당 3~4달러(AWS p4d.24xlarge 기준)에 달하고, 대규모 추론 서비스를 24시간 운영하면 월 수백만 원이 순식간에 소진됩니다. 이 글에서는 양자화(Quantization), 동적 배칭(Dynamic Batching), GPU 오토스케일링과 스팟 인스턴스 활용이라는 세 가지 실전 전략을 통해 GPU 비용을 현실적으로 30~70% 절감하는 방법을 정리합니다. 각 기법은 독립적으로도 효과가 있지만, 조합하면 시너지가 극대화됩니다.
1. 양자화(Quantization): 정밀도를 낮춰 메모리와 속도를 얻는다
양자화는 모델 가중치와 활성값의 수치 표현 비트 수를 줄이는 기법입니다. FP32(32비트 부동소수점)에서 FP16, BF16, INT8, INT4 순으로 낮출수록 메모리 사용량과 연산량이 줄어들어 같은 GPU에서 더 많은 요청을 처리하거나 더 작은 GPU를 사용할 수 있습니다.
| 정밀도 | 비트 | 메모리 대비 FP32 | 추론 속도(상대) | 정확도 손실 | 주요 용도 |
|---|---|---|---|---|---|
| FP32 | 32 | 1x (기준) | 1x | 없음 | 학습 기준선 |
| FP16 / BF16 | 16 | ~0.5x | 1.5~2x | 미미 (<0.1%) | 학습·추론 표준 |
| INT8 (W8A8) | 8 | ~0.25x | 2~3x | 낮음 (0.1~0.5%) | 대규모 추론 서비스 |
| INT4 (GPTQ/AWQ) | 4 | ~0.125x | 2~4x | 중간 (0.5~2%) | 엣지·비용 극한 절감 |
실무에서는 FP16을 기본으로 사용하고, 추론 전용 서비스에서는 INT8 또는 INT4를 적극 고려합니다. LLaMA-2 70B 모델을 예로 들면, FP16으로는 A100 80GB 두 장(약 140GB)이 필요하지만 INT4(AWQ) 적용 시 단일 A100 한 장(약 35~40GB)으로 구동 가능합니다. GPU 비용이 절반 이하로 떨어지는 것입니다.
INT8 양자화 적용 예시 (bitsandbytes + Transformers)
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch
quant_config = BitsAndBytesConfig(
load_in_8bit=True,
llm_int8_threshold=6.0, # outlier 임계값, 기본 6.0 권장
llm_int8_skip_modules=None, # 특정 레이어 제외 시 지정
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-13b-hf",
quantization_config=quant_config,
device_map="auto",
)
print(model.get_memory_footprint() / 1e9, "GB") # 약 13GB vs FP16 26GB
GPTQ/AWQ 오프라인 양자화 (추론 최적화)
# AutoAWQ 를 이용한 INT4 양자화
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = "meta-llama/Llama-2-7b-hf"
quant_path = "llama-2-7b-awq-int4"
model = AutoAWQForCausalLM.from_pretrained(model_path, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_path)
quant_config = {
"zero_point": True,
"q_group_size": 128,
"w_bit": 4,
"version": "GEMM",
}
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
print("양자화 완료 →", quant_path)
트레이드오프: INT8은 대부분 태스크에서 허용 가능한 수준의 정확도 손실을 보이지만, 수학적 추론이나 코드 생성처럼 정밀한 숫자 계산이 필요한 도메인에서는 INT4보다 INT8을 선택하는 것이 안전합니다. 양자화 후에는 반드시 벤치마크(perplexity, task-specific eval) 비교를 수행하세요.
2. 동적 배칭(Dynamic Batching): 요청을 묶어 GPU 활용률을 높인다
단일 추론 요청은 GPU 수천 개의 코어를 충분히 활용하지 못하는 경우가 많습니다. 동적 배칭은 짧은 시간 창(time window) 안에 들어온 여러 요청을 하나의 배치로 묶어 한 번에 처리함으로써 GPU 활용률(Utilization)을 높이는 기법입니다. 활용률이 20%에서 80%로 올라가면 같은 하드웨어로 4배에 달하는 처리량을 얻을 수 있습니다.
NVIDIA Triton Inference Server 동적 배칭 설정
# model_repository/llama2/config.pbtxt
name: "llama2"
backend: "vllm"
max_batch_size: 32
dynamic_batching {
preferred_batch_size: [ 4, 8, 16 ]
max_queue_delay_microseconds: 5000 # 최대 5ms 대기 후 강제 전송
preserve_ordering: false # 응답 순서 보장 불필요 시 false → 처리량 향상
}
instance_group [
{
count: 1
kind: KIND_GPU
gpus: [ 0 ]
}
]
vLLM의 연속 배칭(Continuous Batching)
전통적인 정적 배칭은 배치 내 가장 긴 시퀀스가 끝날 때까지 모든 요청이 대기해야 합니다. vLLM의 연속 배칭(Continuous Batching, Orca 논문 기반)은 개별 요청이 완료되는 즉시 새 요청을 슬롯에 삽입해 GPU가 항상 최대한 채워진 상태를 유지합니다. 내부 테스트 및 커뮤니티 사례 기준으로 처리량이 정적 배칭 대비 2~10배 향상되는 경우가 보고됩니다(워크로드 특성에 따라 다름).
# vLLM 서버 기동 (연속 배칭 기본 활성화)
python -m vllm.entrypoints.openai.api_server
--model meta-llama/Llama-2-13b-chat-hf
--quantization awq
--max-model-len 4096
--gpu-memory-utilization 0.90
--max-num-seqs 256 # 동시 처리 최대 시퀀스 수
--tensor-parallel-size 1
--port 8000
배칭 크기를 크게 잡을수록 처리량은 늘지만 첫 토큰 지연(TTFT, Time To First Token)이 증가합니다. 실시간 챗봇처럼 TTFT가 중요한 서비스는 max_queue_delay를 5~20ms로 제한하고, 배치 분석처럼 지연보다 처리량이 중요한 경우 100ms 이상으로 늘려 비용을 최소화할 수 있습니다.
3. GPU 오토스케일링과 스팟 인스턴스: 유휴 비용을 없앤다
추론 서비스는 트래픽이 낮 시간대와 야간에 크게 차이가 납니다. 고정 인스턴스를 24시간 켜두면 비수기 유휴 비용이 전체의 30~50%를 차지하기도 합니다. 오토스케일링과 스팟 인스턴스를 조합하면 이 낭비를 크게 줄일 수 있습니다.
Kubernetes + KEDA를 이용한 GPU Pod 오토스케일링
# keda-scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: llm-inference-scaler
spec:
scaleTargetRef:
name: llm-deployment
minReplicaCount: 1 # 야간 최소 1개 유지 (콜드스타트 방지)
maxReplicaCount: 8
cooldownPeriod: 120 # 스케일 다운 대기 120초
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: vllm_requests_waiting
threshold: "10" # 대기 요청 10개 초과 시 스케일 업
query: sum(vllm_num_requests_waiting)
스팟 인스턴스 비용 절감
| 인스턴스 유형 | 온디맨드 (시간) | 스팟 평균 (시간) | 절감율 | 인터럽트 빈도 |
|---|---|---|---|---|
| AWS p3.2xlarge (V100 16GB) | $3.06 | $0.92~1.22 | 60~70% | 낮음 |
| AWS p4d.24xlarge (A100 x8) | $32.77 | $9~14 | 57~73% | 중간 |
| GCP A2-highgpu-1g (A100) | $3.67 | $1.10~1.47 | 60~70% | 낮음 |
스팟 인스턴스는 인터럽트 리스크가 있으므로, 추론 서비스에 적용할 때는 다음 패턴이 안정적입니다: 온디맨드 최소 1개(기저 부하 처리) + 스팟 나머지(탄력 부하). AWS의 경우 Spot Interruption 2분 전 경고를 이용해 드레이닝(Draining)을 자동화할 수 있습니다.
# AWS Auto Scaling Group - 혼합 인스턴스 정책 (Terraform)
resource "aws_autoscaling_group" "llm_asg" {
min_size = 1
max_size = 8
desired_capacity = 2
mixed_instances_policy {
instances_distribution {
on_demand_base_capacity = 1 # 온디맨드 최소 1개
on_demand_percentage_above_base_capacity = 0 # 추가분은 100% 스팟
spot_allocation_strategy = "capacity-optimized"
}
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.gpu.id
version = "$Latest"
}
override {
instance_type = "p3.2xlarge"
}
override {
instance_type = "p3.8xlarge"
}
}
}
}
실전 팁: 세 가지 기법의 조합 전략
- 단계적 적용: 먼저 FP16 + 동적 배칭으로 기준선을 잡고, INT8 양자화를 추가한 후 스팟 오토스케일링 순으로 도입하면 각 단계의 효과를 정확히 측정할 수 있습니다.
- GPU 메모리 버짓 계산: 모델 크기(GB) + KV 캐시(배치 크기 × 시퀀스 길이 × 레이어 수 × 헤드 크기 × 2) + 활성값 오버헤드(~10%)를 합산해 여유 있는 인스턴스를 선택하세요. 메모리 부족은 OOM으로 즉시 발현됩니다.
- 모니터링 지표: GPU 활용률(nvidia-smi, DCGM), 큐 대기 길이, TTFT(P50/P99), 토큰/초(TPS), 비용/1K 토큰을 대시보드에 함께 배치하면 병목을 빠르게 찾을 수 있습니다.
- 콜드스타트 완화: 오토스케일 minReplica를 0으로 설정하면 최대 절감이지만 첫 요청 응답이 수분 지연됩니다. 비동기 작업(배치 API)이 아닌 실시간 API에는 최소 1개를 유지하세요.
- 예약 인스턴스 혼용: 베이스라인 트래픽이 예측 가능하다면 1년 예약 인스턴스(Savings Plan)로 온디맨드 대비 30~40% 추가 절감이 가능합니다. 스팟 + 예약 조합이 실제 비용 최적의 구조입니다.
마무리
GPU 서버 비용 절감은 단일 마법 솔루션이 없습니다. 양자화는 모델 메모리와 연산을 줄이고, 동적 배칭은 GPU 활용률을 끌어올리며, 오토스케일링과 스팟 인스턴스는 유휴 비용을 제거합니다. 세 기법을 순서대로 적용하면 이론적으로 총 비용의 50~80%를 절감할 수 있으며, 실제 프로덕션에서도 30~60% 절감 사례가 다수 보고됩니다. 다만 각 기법은 정확도 손실, 지연 증가, 인터럽트 리스크 등의 트레이드오프를 수반하므로, 서비스 SLA와 비용 목표를 함께 고려해 적절한 균형점을 찾는 것이 핵심입니다.
자주 묻는 질문
Q. INT8 양자화를 적용하면 정확도가 얼마나 떨어지나요?
A. 태스크에 따라 다르지만, 대부분의 NLP 벤치마크(MMLU, HellaSwag 등)에서 FP16 대비 0.1~0.5% 수준의 손실이 나타납니다. 수학 추론(GSM8K)이나 코드 생성처럼 정밀도에 민감한 태스크는 0.5~1% 이상 하락할 수 있으므로, 적용 전 반드시 도메인 특화 벤치마크를 실행해 허용 가능한 수준인지 확인해야 합니다.
Q. 스팟 인스턴스가 갑자기 종료되면 진행 중인 추론 요청은 어떻게 되나요?
A. AWS는 종료 2분 전 메타데이터 엔드포인트와 CloudWatch 이벤트로 경고를 제공합니다. 이를 감지해 로드밸런서에서 해당 인스턴스를 드레이닝(신규 요청 차단) 처리하고, 기존 연결은 타임아웃 전에 완료되도록 graceful shutdown을 구현하면 대부분의 요청 손실을 막을 수 있습니다. LLM처럼 단일 요청이 수 초~수십 초 걸리는 경우는 연결 수준 재시도(클라이언트 측)를 함께 구현하는 것이 권장됩니다.
Q. 동적 배칭의 max_queue_delay를 얼마로 설정하는 것이 좋은가요?
A. 서비스 특성에 따라 다릅니다. 실시간 챗봇처럼 TTFT가 200ms 이하여야 하는 경우 5~20ms가 적절합니다. 문서 요약, 번역처럼 수 초 지연이 허용되는 비동기 API는 100~500ms로 설정해 배치 사이즈를 키우면 처리량과 비용 효율이 크게 개선됩니다. A/B 테스트로 지연-처리량 커브를 그려 최적값을 찾는 것을 권장합니다.