제5부 추론 최적화와 서빙
5.4 TensorRT 정밀도 I — INT8 PTQ·캘리브레이션
5.4.1 이 장에서 배우는 것 / 선수 지식
5.2~5.3에서 TensorRT 엔진을 빌드하고 동적 shape를 다뤘다. 그때 정밀도는 --fp16 정도만 켰다. 이 장은 사면체(5.1)의 정밀도 축을 본격적으로 민다 — INT8 후처리 양자화(PTQ, Post-Training Quantization). 학습을 다시 하지 않고, 캘리브레이션 데이터로 스케일만 정해 가중치·활성화를 8비트 정수로 바꿔 메모리·대역폭을 줄이고 INT8 텐서코어로 처리량을 높인다. 핵심 난제는 "어떻게 정확도를 지키느냐" — 그것이 캘리브레이션이다.
배우는 것:
- 왜 INT8인가 — FP32 대비 4×, FP16 대비 2× 작은 메모리·대역폭, INT8 텐서코어 처리량(1.11~1.12)과 사면체 이득.
- 양자화의 수학 — 실수 범위를 정수
[-127, 127]로 매핑하는 스케일, 대칭/비대칭, per-tensor vs per-channel. - PTQ vs QAT — 재학습 없이(PTQ) 스케일만 정하기 vs 학습으로 양자화를 인지시키기(QAT, 5.5).
- 캘리브레이션 — 대표 데이터로 활성화 분포를 모아 정보 손실을 최소화하는 스케일을 고르는 법(minmax·percentile·KL/엔트로피).
- TensorRT calibrator 구현·캘리브레이션 캐시,
trtexec --int8실습. - 정확도 검증과 민감 레이어 혼합정밀 — 태스크 지표로 판정하고, 안 되면 일부 레이어를 고정밀로 되돌리기.
선수 지식: 1.13~1.14(부동소수·정밀도·양자화 기초 — 이 장의 토대), 5.2~5.3(빌드 흐름·builder flag·정확도 검증 polygraphy), 5.1(정밀도 축·"양자화는 검증과 함께"), 1.11~1.12(INT8 텐서코어). 환경은 5.2과 동일.
정직: TensorRT의 calibrator 클래스·INT8 API는 버전마다 다르다(implicit 캘리브레이션 → explicit Q/DQ로 무게중심 이동, 클래스 개명·deprecation). 이 장은 개념과 흐름을 중심에 두고, 코드는 최근 계열의 한 방식을 예시한다 — 정확한 클래스·시그니처는 당신 버전 문서로 확인. 정확도·속도 수치는 모두 환경 의존 개략값, 직접 실측.
5.4.2 왜 INT8인가 — 사면체 이득과 대가
INT8이 사면체를 미는 방식:
- 메모리·대역폭 ↓: 가중치·활성화가 1바이트 → FP16의 절반, FP32의 1/4. decode·메모리 바운드 구간(5.1.4)에서 읽을 바이트가 줄어 빨라지고, KV·활성화 메모리가 줄어 동시 요청 수↑.
- 처리량 ↑: INT8 텐서코어(1.11~1.12)는 같은 면적에서 더 많은 정수 MAC을 처리 → 컴퓨트 바운드 레이어도 빨라짐.
- 대가 = 정밀도: 8비트는 표현 가능한 값이 256개뿐. 범위를 잘못 잡으면 클리핑(범위 밖 잘림) 또는 양자화 잡음(해상도 부족) 으로 정확도가 무너진다. 이 대가를 관리하는 것이 캘리브레이션.
핵심: INT8은 "무비용 속도"가 아니다. 반드시 태스크 지표로 정확도를 검증하고(5.1.11, 5.2.6), 통과할 때만 켠다. 분류처럼 견고한 태스크는 정확도를 유지하고, 회귀·생성·민감한 검출은 더 조심해야 한다.
5.4.3 양자화의 수학 — 스케일·대칭·per-channel
대칭 선형 양자화(TensorRT가 주로 쓰는 방식)의 핵심 한 줄:
q = round( clip(x, -t, +t) / scale ), scale = t / 127
역양자화: x' = q * scale
여기서 t = '동적 범위(dynamic range)' 임계값 = 그 텐서에서 표현할 최대 절댓값
- scale(스케일): 실수 1 LSB가 정수로 얼마인지.
t(범위)를 127등분.t를 어떻게 고르냐가 캘리브레이션의 전부. - 대칭(symmetric): 0을 중심으로
[-t, +t]. 영점(zero-point)=0이라 곱셈이 단순·빠르다. (비대칭은 영점≠0으로 한쪽으로 치우친 분포에 유리하나 TRT는 대칭 중심.) - 클리핑 vs 해상도 트레이드오프:
t를 크게(예: 최대 절댓값) → 클리핑 없음, 그러나 흔한 값들이 적은 정수 칸을 써 해상도 손실(양자화 잡음↑).t를 작게(이상치 무시) → 흔한 값에 해상도 집중, 그러나 큰 값이 클리핑됨.- 최적
t는 둘 사이 — 정보 손실을 최소화하는 지점(KL 캘리브레이션의 동기).
t를 어디에 긋느냐가 관건이다: t_big(이상치까지 포함) → 해상도 낭비, t_kl(정보손실 최소, KL) → 보통 최선, t_small(이상치 클리핑) → 큰 값 손실.
per-tensor vs per-channel (매우 중요):
| 방식 | 스케일 개수 | 어디에 |
|---|---|---|
| per-tensor | 텐서당 1개 | 주로 활성화(런타임 가변, 캘리브레이션으로 추정) |
| per-channel | 출력 채널당 1개 | 주로 가중치(채널별 분포 차이가 커서 채널마다 스케일이라야 정확) |
가중치를 per-tensor로 양자화하면 채널 간 스케일 차이를 한 값으로 합쳐 정확도가 크게 떨어진다 — 가중치는 per-channel이 정석(TRT가 자동으로 적용). 활성화는 채널별로 분리하기 어려워 보통 per-tensor + 캘리브레이션.
5.4.4 PTQ vs QAT — 두 갈래
INT8로 가는 두 길:
대부분 PTQ로 시작, 정확도 부족하면 QAT로.
- PTQ: 가장 적은 노력. 학습 파이프라인·라벨 불필요(캘리브레이션은 라벨 없는 대표 입력이면 충분). 정확도 손실이 허용 범위면 끝.
- QAT(5.5): PTQ로 정확도가 모자랄 때. 학습 중 양자화를 시뮬레이션해 모델이 적응 → 보통 PTQ보다 정확하나 재학습 비용.
견해: PTQ를 먼저 시도하라 — 비용이 낮고 빠르다. 분류·다수 CNN은 PTQ로 충분할 때가 많다. 태스크 지표가 임계 이상 떨어지면(예: top-1 −1%p 초과), 민감 레이어 혼합정밀(5.4.8)을 먼저 시도하고, 그래도 안 되면 QAT(5.5).
5.4.5 캘리브레이션의 작동 — TensorRT 내부
활성화의 동적 범위 t는 데이터에 의존한다(런타임에 흐르는 값의 분포). TensorRT는 캘리브레이션 데이터셋을 모델에 흘려 각 활성화 텐서의 분포를 수집하고 t를 정한다.
KL(엔트로피) 캘리브레이션(TensorRT의 대표 방식)의 요지: 원래 FP 분포 P와, t로 잘라 INT8로 양자화한 분포 Q를 비교해, 두 분포의 정보 차이(KL divergence)가 가장 작은 t 를 고른다. "이상치를 조금 클리핑하더라도 전체 분포 모양을 가장 잘 보존하는" 지점이다 — minmax(이상치에 끌려감)보다 대개 정확.
| 캘리브레이터(개념) | 특징 | 적합 |
|---|---|---|
| Entropy(KL) 계열 | 정보손실 최소, 분포 보존 | 일반적 기본(대부분 CNN) |
| MinMax 계열 | 관측 최대로 범위, 클리핑 없음 | 범위가 중요한 일부 연산·이상치 적을 때 |
| Percentile | 분위수로 이상치 제외 | 이상치(아웃라이어)가 두드러질 때 |
캘리브레이션 데이터의 조건(가장 중요):
- 대표성: 실제 추론에서 들어올 입력 분포를 닮아야 한다. 학습셋 일부·검증셋 일부가 흔히 쓰인다. 편향된 데이터(예: 한 클래스만) 로 캘리브레이션하면 스케일이 틀어진다.
- 양: 보통 수백~수천 샘플(태스크·모델 의존). 너무 적으면 통계 부정확, 너무 많으면 캘리브레이션만 느려짐.
- 라벨 불필요: 캘리브레이션은 활성화 분포만 보므로 라벨이 없어도 된다(검증은 별개로 라벨 필요).
5.4.6 워크스루 (1) — trtexec로 INT8 빠르게
가장 빠른 출발. 단, 캘리브레이션 없이 --int8만 주면 TRT가 임의/단순 범위를 쓸 수 있어 정확도 보장이 안 된다 — 빠른 성능 가늠용으로만.
# 성능 가늠용(정확도 비보장): INT8 텐서코어가 주는 속도 상한을 본다
trtexec --onnx=resnet50.onnx --saveEngine=r50_int8_perf.plan \
--int8 --optShapes=input:8x3x224x224
# FP16 엔진(5.2)과 Throughput·Latency·엔진 크기 비교 → INT8 잠재 이득 확인
정확도까지 보려면 캘리브레이션 캐시를 함께 준다. 캐시는 Python calibrator(5.4.7)로 한 번 만들어 두고 재사용:
# 캘리브레이션 캐시를 사용해 빌드(정확도 의미 있는 INT8 엔진)
trtexec --onnx=resnet50.onnx --saveEngine=r50_int8.plan \
--int8 --calib=resnet50_calib.cache \
--optShapes=input:8x3x224x224
체크:
--int8만으로 빠르다고 그대로 배포하면 안 된다. 반드시 캘리브레이션 + 태스크 지표 검증(5.4.9). trtexec의 INT8 단독은 "얼마나 빨라질 수 있나"의 상한 탐색일 뿐.
5.4.7 워크스루 (2) — Python calibrator 구현
PTQ의 본체. 대표 데이터를 배치로 공급하는 calibrator를 구현해 빌드에 연결한다. (클래스명·메서드는 버전 의존 — 흐름에 집중.)
# calibrator.py — Entropy 캘리브레이터(개념 예시; 버전별 API 확인)
import os, numpy as np, tensorrt as trt
from cuda import cudart
class ImageCalibrator(trt.IInt8EntropyCalibrator2):
"""대표 이미지 배치를 TRT에 공급해 활성화 스케일을 추정.
cache가 있으면 데이터 없이 재사용(재빌드 가속)."""
def __init__(self, data, batch=8, cache="resnet50_calib.cache"):
super().__init__()
self.data = data # np.float32 (M,3,224,224) — 전처리까지 끝낸 대표 입력
self.batch = batch
self.cache_path = cache
self.idx = 0
nbytes = batch*3*224*224*4
_, self.d_in = cudart.cudaMalloc(nbytes)
def get_batch_size(self): return self.batch
def get_batch(self, names): # 다음 배치를 GPU로 올려 포인터 반환. 끝나면 None
if self.idx + self.batch > len(self.data):
return None
chunk = np.ascontiguousarray(self.data[self.idx:self.idx+self.batch])
cudart.cudaMemcpy(self.d_in, chunk.ctypes.data, chunk.nbytes,
cudart.cudaMemcpyKind.cudaMemcpyHostToDevice)
self.idx += self.batch
return [int(self.d_in)]
def read_calibration_cache(self): # 캐시 있으면 캘리브레이션 생략
if os.path.exists(self.cache_path):
with open(self.cache_path, "rb") as f: return f.read()
return None
def write_calibration_cache(self, cache):
with open(self.cache_path, "wb") as f: f.write(cache)
# build_int8.py — calibrator를 붙여 INT8 엔진 빌드
import tensorrt as trt, numpy as np
from calibrator import ImageCalibrator
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
def build_int8(onnx_path, plan_path, calib_data):
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, TRT_LOGGER)
with open(onnx_path, "rb") as f:
assert parser.parse(f.read()), "ONNX parse failed"
config = builder.create_builder_config()
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 << 30)
config.set_flag(trt.BuilderFlag.INT8) # ★ INT8 활성화
# (권장) FP16도 함께 켜 INT8 부적합 레이어가 FP16로 떨어지게 → 정확도·속도 균형
config.set_flag(trt.BuilderFlag.FP16)
config.int8_calibrator = ImageCalibrator(calib_data) # ★ 캘리브레이터 연결
prof = builder.create_optimization_profile()
prof.set_shape("input", (1,3,224,224), (8,3,224,224), (32,3,224,224))
config.add_optimization_profile(prof)
# INT8 캘리브레이션은 보통 단일 형상(calibration profile) 기준 — 버전별 규칙 확인
serialized = builder.build_serialized_network(network, config)
assert serialized is not None, "INT8 build failed"
with open(plan_path, "wb") as f: f.write(serialized)
print("wrote", plan_path)
if __name__ == "__main__":
# 대표 입력 ~512장(전처리 완료). 실제론 검증셋 일부를 모델 입력 형태로.
calib = np.random.randn(512,3,224,224).astype(np.float32) # ← 실데이터로 교체
build_int8("resnet50.onnx", "resnet50_int8.plan", calib)
python build_int8.py # 첫 실행: 캘리브레이션 수행 + resnet50_calib.cache 생성
# 재실행: 캐시 재사용으로 캘리브레이션 생략(빌드 가속)
정직:
np.random은 대표 데이터가 아니다 — 데모용 자리표시일 뿐. 실제로는 검증셋/운영 로그의 실제 입력을 전처리해 넣어야 스케일이 의미 있다. 무작위 데이터로 캘리브레이션하면 정확도가 망가진다.
5.4.8 워크스루 (3) — 정확도 검증·민감 레이어 혼합정밀
INT8 엔진은 반드시 태스크 지표로 검증한다(5.1.11, 5.2.6).
# 1) 대표 평가셋으로 FP32(또는 FP16) 대비 정확도 비교 — '수치 오차'가 아니라 '태스크 지표'
# 분류: top-1/top-5, 검출: mAP, 세그: mIoU, 생성: 해당 지표
python eval_accuracy.py --engine resnet50_int8.plan --metric top1
python eval_accuracy.py --engine resnet50_fp16.plan --metric top1
# 예: FP16 top1=76.1%, INT8 top1=75.7% → -0.4%p. 허용선(예: -1%p) 안이면 통과(개략)
정확도가 임계 밖이면 민감 레이어를 고정밀로 되돌리는 혼합정밀:
# 특정 레이어의 정밀도를 강제(개념; API는 버전 확인)
for i in range(network.num_layers):
layer = network.get_layer(i)
if layer.name in SENSITIVE_LAYERS: # 민감도 분석으로 찾은 레이어들
layer.precision = trt.float16 # 이 레이어는 INT8 대신 FP16
layer.set_output_type(0, trt.float16)
config.set_flag(trt.BuilderFlag.OBEY_PRECISION_CONSTRAINTS) # 강제 준수(버전별 플래그)
흔한 민감 지점: 모델의 첫 레이어와 마지막(분류) 레이어, 값 범위가 넓은 정규화·소프트맥스 직전, 어텐션 일부. 전부 INT8로 밀지 말고 소수 민감 레이어만 고정밀로 두면 "거의 INT8 속도 + 정확도 회복"을 얻을 때가 많다.
5.4.9 중간 점검 (체크포인트)
- [ ] INT8이 사면체의 어느 축(메모리·처리량)을 어떻게 밀고, 대가(정밀도)가 무엇인지 설명한다.
- [ ] 대칭 양자화의 scale = t/127, 클리핑 vs 해상도 트레이드오프, 가중치=per-channel·활성화=per-tensor의 이유를 안다.
- [ ] PTQ vs QAT를 구분하고 PTQ를 먼저 쓰는 이유를 제시한다.
- [ ] 캘리브레이션이 대표 데이터로 활성화 분포를 모아 t를 정하는 것임을, KL/minmax/percentile의 차이와 함께 설명한다.
- [ ] Python calibrator를 구현·연결해 INT8 엔진을 빌드하고, 캘리브레이션 캐시를 재사용한다.
- [ ] 태스크 지표로 정확도를 검증하고, 임계 밖이면 민감 레이어 혼합정밀로 회복한다.
증상→의심: INT8 정확도가 크게 떨어짐 → 캘리브레이션 데이터 비대표/부족, 또는 가중치 per-tensor·민감 레이어까지 INT8 → 데이터 교체·per-channel 확인·민감 레이어 FP16. INT8인데 별로 안 빠름 → INT8 텐서코어 미사용(형상·세대), 또는 많은 레이어가 FP16로 폴백 → 프로파일·레이어 정밀도 확인. 재빌드마다 캘리브레이션 반복(느림) → 캐시 미사용. trtexec --int8 단독이 부정확 → 캘리브레이션 캐시 없이 빌드한 것(정상; 캐시 제공 필요).
5.4.10 트레이드오프·대안
어느 정밀도로? 사면체 위 선택.
| 정밀도 | 누구에게 | 비고 |
|---|---|---|
| FP16/BF16 | 정확도 민감·노력 최소 | 안전한 기본. 텐서코어 가속, 정확도 손실 거의 없음 |
| INT8 PTQ | 처리량·메모리 압박, 견고한 태스크 | 노력 적음, 정확도 검증 필수. 대다수 CNN OK |
| INT8 QAT(5.5) | PTQ로 정확도 부족할 때 | 재학습 비용, 정확도 최선 |
| FP8(4.2·5.5) | Hopper+ 세대, LLM·트랜스포머 | 세대 의존. INT8보다 트랜스포머에 유리한 경우 |
| 혼합(INT8+FP16) | INT8이 일부 레이어에서만 망가질 때 | 민감 레이어만 고정밀 — 실무 단골 |
견해: FP16으로 안전하게 시작 → 처리량/비용이 아프면 INT8 PTQ 시도 → 정확도 부족분은 혼합정밀 → 그래도 안 되면 QAT. 이 사다리를 한 칸씩 올라가는 것이 노력 대비 효율적이다. 트랜스포머·LLM은 INT8보다 FP8(5.5·4.2)이 더 자연스러운 경우가 많다 — 텐서코어·트랜스포머 엔진 지원과 함께 판단.
비엔비디아 정직: 양자화는 TRT만의 것이 아니다 — ONNX Runtime·다른 런타임도 INT8 PTQ/QAT를 지원하고, 양자화 표현(Q/DQ ONNX 노드)은 표준에 가깝다. TRT의 강점은 INT8 텐서코어·per-channel·엔트로피 캘리브레이션·혼합정밀의 통합과 그 GPU에서의 오토튜닝이다. 정확도·속도는 반드시 당신 모델·평가셋으로 실측.
5.4.11 흔한 함정
- 정확도를 수치 오차로 판정. INT8은 원소별 오차가 크다(정상).
--atol비교로 FAIL을 보고 포기하지 말 것 — 태스크 지표(top-1·mAP 등)로 판정(5.2.11 연습3). - 비대표 캘리브레이션 데이터. 무작위·편향(한 클래스)·전처리 불일치 데이터로 캘리브레이션하면 스케일이 틀어져 정확도 붕괴. 실제 입력 분포 + 동일 전처리.
- 캘리브레이션 데이터 전처리 불일치. 추론 시 정규화·리사이즈와 다른 전처리로 캘리브레이션하면 활성화 분포가 달라 스케일이 어긋난다. 추론과 똑같이.
- 가중치를 per-tensor로. 가중치는 채널별 분포 차가 커서 per-channel이라야 한다(TRT 기본). 임의로 끄지 말 것.
- 모든 레이어를 INT8로 강행. 첫·마지막·민감 레이어까지 INT8로 밀면 정확도가 무너진다. 민감 레이어는 FP16/FP32 혼합.
- 캐시를 GPU/모델/전처리 바뀌어도 재사용. 캘리브레이션 캐시는 그 모델·그 데이터 분포 기준. 모델/전처리가 바뀌면 재캘리브레이션.
--int8단독을 정확도 보장으로 오해. 캘리브레이션 없는 INT8은 성능 가늠용. 배포엔 캘리브레이션+검증.- 검증을 캘리브레이션 데이터로. 캘리브레이션에 쓴 데이터로 정확도를 재면 낙관 편향. 분리된 평가셋으로.
5.4.12 연습문제
연습 1 (양자화 수학). 어떤 활성화 텐서의 값이 대부분 [-2, 2]인데 드물게 8까지 벗어난다. (a) MinMax로 t를 잡으면 흔한 값들의 해상도는? (b) KL/Percentile로 $t\approx2.5$를 잡으면 8은 어떻게 되나? (c) 어느 쪽이 보통 정확도에 유리한가, 왜?
정답
(a) MinMax는 t=8 → scale=8/127≈0.063. 대부분의 값 [-2,2]는 정수 약 ±32칸만 써 해상도가 낮다(이상치 8 때문에 흔한 값이 손해). (b) $t\approx2.5$ → 8은 +127로 클리핑(소수 이상치 손실). 흔한 값 [-2,2]는 약 ±100칸을 써 해상도가 높다. (c) 보통 KL/Percentile이 유리 — 소수 이상치를 약간 클리핑하더라도 흔한 값의 해상도를 살려 전체 정보 손실(분포 보존)이 작다. 이것이 TRT가 엔트로피 캘리브레이션을 기본 계열로 두는 이유. (단, 이상치가 의미 있는 태스크면 percentile 분위수·범위 조정 필요.)
연습 2 (per-channel). 합성곱 가중치를 per-tensor로 양자화했더니 정확도가 크게 떨어졌다. 원인과 해결, 그리고 활성화는 보통 per-tensor인 이유.
정답
원인: 출력 채널마다 가중치 분포(범위)가 크게 달라, 한 스케일로 모든 채널을 하나로 합치면 작은 범위 채널은 해상도 손실, 큰 범위 채널은 클리핑 → 정확도 붕괴. 해결: per-channel 양자화(채널별 스케일) — TRT 기본. 활성화가 per-tensor인 이유: 활성화는 런타임에 가변이고 채널별로 분리해 캘리브레이션하기 어렵고 비용이 큼. 가중치는 빌드 시점에 값이 고정돼 채널별 스케일을 정확히 계산할 수 있어 per-channel이 자연스럽다.
연습 3 (캘리브레이션 데이터). 동료가 학습셋에서 "고양이 클래스 이미지 1000장"만으로 캘리브레이션해 다중 클래스 분류 INT8 엔진을 만들었더니 다른 클래스 정확도가 나쁘다. 무엇이 문제이고 어떻게 고치나? 데이터 양·라벨에 대한 조언도.
정답
문제: 비대표 캘리브레이션 데이터(한 클래스만) → 활성화 분포가 실제 추론 분포(전 클래스)와 달라 스케일이 한쪽으로 편향 → 다른 클래스에서 클리핑/해상도 손실. 고치기: 전 클래스를 고루 포함한 대표 입력(검증셋 무작위 샘플 등)으로 재캘리브레이션, 추론과 동일한 전처리 적용. 양: 보통 수백~수천 샘플로 충분(태스크 의존, 너무 적으면 통계 부정확). 라벨: 캘리브레이션은 활성화 분포만 보므로 라벨 불필요 — 단, 최종 정확도 검증은 라벨 있는 별도 평가셋으로(캘리브레이션 데이터와 분리).
연습 4 (혼합정밀). INT8 PTQ로 검출 모델 mAP가 기준 대비 −3%p 떨어졌다(허용 −1%p). QAT로 바로 가기 전에 시도할 단계와, 어떤 레이어를 의심할지.
정답
QAT 전에 민감 레이어 혼합정밀을 시도: ① 레이어별 민감도 분석(한 레이어씩 INT8↔FP16 전환해 mAP 영향 측정) → ② 가장 민감한 소수 레이어를 FP16/FP32로 강제, 나머지 INT8 유지. 의심 레이어: 첫 입력 레이어(원시 입력 분포가 넓음), 마지막 검출 헤드/회귀 출력(좌표·신뢰도는 해상도 민감), 값 범위가 넓은 정규화·소프트맥스 직전, 특정 어텐션. 이렇게 소수 레이어만 고정밀로 두면 대개 속도 대부분 유지하며 정확도 회복. 그래도 부족하면 QAT(5.5).
연습 5 (정밀도 사다리). 새 트랜스포머 기반 추론 서비스의 비용을 낮추라는 요구다. FP16에서 시작해 어떤 순서로 정밀도를 내릴지, 각 단계의 검증·중단 기준과 함께 설계하라.
정답
① FP16/BF16 기준 확보(정확도·지연·처리량·$ 측정). ② FP8 우선 검토(Hopper+ 세대면) — 트랜스포머는 INT8보다 FP8이 정확도·속도 균형이 좋은 경우가 많음(5.5·4.2). 대표 평가셋으로 태스크 지표 비교, 허용 하락 안이면 채택. ③ FP8이 불가/부족하면 INT8 PTQ + 캘리브레이션 → 태스크 지표 검증. ④ 정확도 부족분은 혼합정밀(민감 레이어 고정밀) → 그래도 안 되면 QAT. 각 단계 중단 기준: 태스크 지표 하락이 허용선(예: −1%p) 초과면 그 단계 롤백 또는 혼합정밀로 보정. 모든 결정은 그 하드웨어·그 모델·그 평가셋 실측으로, 슬라이드 수치 금지.
5.4.13 요약 · 다음 장
- INT8 PTQ는 재학습 없이 캘리브레이션으로 스케일만 정해 메모리 1/4(vs FP32)·처리량↑를 얻는다. 대가는 정밀도 — 반드시 태스크 지표로 검증.
- 양자화 =
q = round(clip(x,−t,t)/scale),scale=t/127.t(동적 범위) 선택이 캘리브레이션의 전부 — 클리핑 vs 해상도의 균형. 가중치 per-channel·활성화 per-tensor. - 캘리브레이션은 대표 데이터로 활성화 분포를 모아
t를 정한다. KL/엔트로피(분포 보존)가 기본 계열, minmax·percentile은 상황별. 데이터는 실제 분포·동일 전처리·수백~수천 장·라벨 불필요. - 실습: calibrator 구현·연결 → INT8 빌드 → 캘리브레이션 캐시 재사용 → 태스크 지표 검증 → 부족하면 민감 레이어 혼합정밀. PTQ로 부족하면 QAT.
- 정직: API·클래스는 버전 의존,
--int8단독은 정확도 비보장, 모든 수치 실측, 검증은 분리된 평가셋으로.
다음 장: 5.5 TensorRT 정밀도 II — QAT·FP8·정확도 검증. PTQ로 부족할 때의 QAT(학습 인지 양자화) — 학습 그래프에 가짜양자화(Q/DQ)를 넣어 모델이 양자화에 적응하게 하는 법, FP8(Hopper+ 트랜스포머)과 INT8의 선택, 그리고 정확도 검증을 체계화한다.