๐ English Abstract
This article provides a comprehensive troubleshooting guide for resolving catastrophic model accuracy degradation during TensorRT INT8 quantization. We explore why Post-Training Quantization (PTQ) fails due to calibration data bias and outlier activation distributions. Furthermore, it details the step-by-step transition to Quantization-Aware Training (QAT) using PyTorch and NVIDIA TensorRT QAT Toolkit to restore inference accuracy while maintaining high hardware throughput.
๋ฅ๋ฌ๋ ๋ชจ๋ธ์ ์ค์ ํ๋ก๋์ ์๋ฒ๋ ์ฃ์ง ๋๋ฐ์ด์ค(Edge Device)์ ๋ฐฐํฌํ ๋, ์ถ๋ก ์๋(Inference Latency) ๋จ์ถ๊ณผ ๋ฉ๋ชจ๋ฆฌ ๋์ญํญ(Memory Bandwidth) ์ ๊ฐ์ ํ์์ ๊ฐ๊น์ต๋๋ค. NVIDIA์ TensorRT๋ FP32 ๋๋ FP16 ์ ๋ฐ๋(Precision)์ ๋ชจ๋ธ์ INT8(8-bit Integer) ์ ๋ฐ๋๋ก ์์ํ(Quantization)ํ์ฌ ์ต๋ 2~4๋ฐฐ ์ด์์ ์ฒ๋ฆฌ๋(Throughput) ํฅ์์ ์ ๊ณตํฉ๋๋ค.
ํ์ง๋ง ํ๋ จ์ด ๋๋ ๋ชจ๋ธ์ ์์ฝ๊ฒ ์ ์ฉํ ์ ์๋ PTQ(Post-Training Quantization, ํ๋ จ ํ ์์ํ) ๋ฐฉ์์ ์ ์ฉํ๋ฉด, ํน์ ๋ฅ๋ฌ๋ ์ํคํ ์ฒ(Vision Transformer, YOLOv8, Large Language Models ๋ฑ)์์ ๋ชจ๋ธ ์ถ๋ก ์ ํ๋(Accuracy)๊ฐ ๊ฐ๋นํ๊ธฐ ํ๋ค ์ ๋๋ก ๊ธ๊ฐํ๋ ํ์์ด ๋น๋ฒํ ๋ฐ์ํฉ๋๋ค. ๋ณธ ํฌ์คํ ์์๋ PTQ ์บ๋ฆฌ๋ธ๋ ์ด์ ์ค์ฐจ๊ฐ ๋ฐ์ํ๋ ๊ทผ๋ณธ ์์ธ์ ๋ถ์ํ๊ณ , ์ด๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด QAT(Quantization-Aware Training, ์์ํ ์ธ์ง ํ์ต) ๋ชจ๋์ ๊ตฌ์ถํ๊ณ TensorRT ํ์ดํ๋ผ์ธ์ผ๋ก ์ต์ ํํ๋ ์ค๋ฌด ํธ๋ฌ๋ธ์ํ ๊ณผ์ ์ ๊ธฐ์ ํฉ๋๋ค.
1. PTQ(Post-Training Quantization) ์บ๋ฆฌ๋ธ๋ ์ด์ ์ค์ฐจ์ ๊ทผ๋ณธ ์์ธ ๋ถ์
PTQ๋ ์ถ๊ฐ์ ์ธ ๋ชจ๋ธ ์ฌํ์ต ์์ด ์๋์ ์บ๋ฆฌ๋ธ๋ ์ด์ ๋ฐ์ดํฐ์ (Calibration Dataset)๋ง์ ํ์ฉํ์ฌ 32๋นํธ ๋ถ๋์์์ (FP32) ํ์ฑํ(Activation) ๊ฐ์ ๋์ ๋ฒ์(Dynamic Range)๋ฅผ ์ธก์ ํ๊ณ , ์ด๋ฅผ 8๋นํธ ์ ์(INT8) ์์ญ[-128, 127] ๋๋ [0, 255]๋ก ๋งคํ(Mapping)ํฉ๋๋ค.
1) ์ด์์น ํ์ฑํ(Outlier Activation)์ ๋ฒ์ ์์ถ ํ์
๋๋ถ๋ถ์ ๋ฅ๋ฌ๋ ๋ ์ด์ด ํ์ฑํ ๊ฐ ๋ถํฌ๋ ์ ๊ท๋ถํฌ(Normal Distribution)์ ๊ฐ๊น์ด ํํ๋ฅผ ๋ ์ง๋ง, ์ต์ ์ปดํฌ๋ณผ๋ฃจ์ (CNN)์ด๋ ํธ๋์คํฌ๋จธ(Transformer) ๋ชจ๋ธ์ ๊ฒฝ์ฐ ํน์ ์ฑ๋์์ ์ด์์น(Outlier) ๊ฐ์ด ํญ๋ฐ์ ์ผ๋ก ๋ฐ์ํ๋ ํน์ฑ์ ๋ณด์ ๋๋ค. PTQ ์บ๋ฆฌ๋ธ๋ ์ดํฐ(Calibrator)๊ฐ ๊ทน๋จ์ ์ธ ์ต์/์ต๋(MinMax) ๊ธฐ์ค์ ์ก์ ๊ฒฝ์ฐ, ๋ฏธ์ธํ ์ฃผ์ ๊ฐ์ค์น ๊ฐ๋ค์ด ๋ชจ๋ ํ๋์ INT8 ๋น(Bin)์ผ๋ก ์์ํ๋์ด ์ฌ๊ฐํ ์ ๋ณด ์์ค(Quantization Error)์ด ๋ฐ์ํฉ๋๋ค.
2) TensorRT Calibrator ์๊ณ ๋ฆฌ์ฆ์ ํ๊ณ
TensorRT๋ ๋ค์ํ ์บ๋ฆฌ๋ธ๋ ์ด์ ์๊ณ ๋ฆฌ์ฆ์ ์ ๊ณตํฉ๋๋ค.
- IInt8MinMaxCalibrator: ์ต๋๊ฐ๊ณผ ์ต์๊ฐ์ ๋จ์ ๋งคํํ๋ฏ๋ก ์ด์์น์ ๋งค์ฐ ์ทจ์ฝํฉ๋๋ค.
- IInt8EntropyCalibrator2: FP32 ํ์ฑํ ๋ถํฌ์ INT8 ๋ํ ๋ถํฌ ๊ฐ์ KL-Divergence(Kullback-Leibler Divergence)๋ฅผ ์ต์ํํ๋๋ก ์ค์ผ์ผ ํฉํฐ(Scale Factor)๋ฅผ ๊ณ์ฐํฉ๋๋ค. ์ ๋ณด ์์ค์ ์ค์ด์ง๋ง, ํ์ฑํ ๋ถํฌ๊ฐ ์ฅ๊ผฌ๋ฆฌ(Long-tailed) ํํ์ผ ๊ฒฝ์ฐ ์ค์ผ์ผ ์ฐ์ถ์ ์ค์ฐจ๊ฐ ๋ฐ์ํฉ๋๋ค.
3) ์์ฐจ ์ฐ๊ฒฐ(Residual Connections) ๋ฐ ์คํต ์ฐ๊ฒฐ์ ์ค์ผ์ผ ๋ถ์ผ์น
ResNet ๋๋ Feature Pyramid Network(FPN) ๊ตฌ์กฐ์ฒ๋ผ ๋ ๊ฐ์ ํ ์๊ฐ ๋ง์ (Addition) ๋๋ ๊ฒฐํฉ(Concatenation)๋๋ ๊ตฌ์กฐ์์๋ ๋ ๋ ์ด์ด ๊ฐ์ ์์ํ ์ค์ผ์ผ ํฉํฐ๊ฐ ์๋ก ๋ค๋ฅด๋ฉด ์ฌ์ ๋ ฌ ๊ณผ์ ์์ ์ถ๊ฐ์ ์ธ ์์ํ ์ค์ฐจ๊ฐ ๋์ ๋๋ ์ฃ์ง ์ผ์ด์ค(Edge Cases)๊ฐ ๋ฐ์ํฉ๋๋ค.
2. ๋ ์ด์ด๋ณ ์ ํ๋ ์ง๋จ ๋ฐ ๋ณ๋ชฉ ๋ ์ด์ด ํ์ (Troubleshooting Workflow)
QAT๋ก ์ ํํ๊ธฐ ์ , ์ ํ๋ ๊ธ๊ฐ์ ์์ธ์ด ์ ๋ฐ์ ์ธ ๋ฌธ์ ์ธ์ง, ํน์ ํน์ ๋ ์ด์ด์ ๋ถ๊ดด ๋๋ฌธ์ธ์ง๋ฅผ ์ ํํ ์ง๋จํด์ผ ํฉ๋๋ค.
์ง๋จ 1: TensorRT Polygraphy ๋ฐ Layer Profiler ํ์ฉ
NVIDIA Polygraphy ๋๊ตฌ๋ฅผ ํ์ฉํ๋ฉด FP32 ์์ง๊ณผ INT8 ์์ง์ ์ค๊ฐ ๋ ์ด์ด ์ถ๋ ฅ ํ ์ ๊ฐ์ SQNR(Signal-to-Quantization-Noise Ratio) ๋๋ Cosine Similarity(์ฝ์ฌ์ธ ์ ์ฌ๋)๋ฅผ ๊ณ์ฐํ์ฌ ์ ๋ฐํ๊ฒ ์ค์ฐจ ์ง์ ์ ํ๊ฒฉํ ์ ์์ต๋๋ค.
- ์ค์ฐจ ๊ฒ์ฆ ๋ช
๋ น ์์:
polygraphy run model.onnx --trt --int8 --calibration-cache=calib.cache --onnxrt --int8-precision-error-check - ๊ฒฐ๊ณผ ํด์: Cosine Similarity๊ฐ 0.90 ๋ฏธ๋ง์ผ๋ก ๋จ์ด์ง๋ ์ต์ด์ Convolution ๋๋ Matrix Multiplication ๋ ์ด์ด๊ฐ ์บ๋ฆฌ๋ธ๋ ์ด์ ์คํจ์ ์ฃผ์ ์์ธ์ ๋๋ค.
์ง๋จ 2: ํผํฉ ์ ๋ฐ๋(Mixed Precision Fallback) ์์ ์ ์ฉ
์์์ด ์ฌํ ๋ ์ด์ด์ ํํด FP16 ์ ๋ฐ๋๋ก ๊ฐ์ ๋ณ๊ฒฝ(Fallback)์ ์ ์ฉํ์ ๋ ์ ํ๋๊ฐ ๋ณต๊ตฌ๋๋์ง ํ์ธํฉ๋๋ค. ๋ง์ฝ ์ ์ฒด ๋ ์ด์ด ์ค 5% ๋ฏธ๋ง์ ๋ ์ด์ด๋ง FP16์ผ๋ก ์ ์งํ์ฌ ์ ํ๋๊ฐ ๋ณต๊ตฌ๋๋ค๋ฉด ํผํฉ ์ ๋ฐ๋ ์ ๋ต์ ์ทจํ ์ ์์ผ๋, ์์คํ ์ ์ฒด ์ถ๋ก latency ๋ชฉํ๋ฅผ ๋ฌ์ฑํ๊ธฐ ์ด๋ ต๋ค๋ฉด ์์ ํ QAT(Quantization-Aware Training) ์ ์ฉ์ด ํ์๊ฐ ๋ฉ๋๋ค.
3. QAT(Quantization-Aware Training)๋ฅผ ์ด์ฉํ ์ฑ๋ฅ ์ต์ ํ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
QAT๋ ํ์ดํ ์น(PyTorch) ๋ฑ์ ์์ ํ(Forward Pass) ๊ณผ์ ์ ๊ฐ์ง ์์ํ(Fake Quantization, FakeQuant) ๋ ธ๋๋ฅผ ์ฝ์ ํ์ฌ, ์์ํ ์ค์ฐจ์ ๋ฐ์ฌ๋ฆผ(Rounding)์ ์ํ ์์ค์ ์ค์ฐจ ์ญ์ ํ(Backpropagation) ๊ณผ์ ์ ๋ฐ์ํจ์ผ๋ก์จ ๋ชจ๋ธ ๊ฐ์ค์น๋ฅผ ์์ํ ์นํ์ ์ผ๋ก ์ฌํ์ต(Fine-tuning)์ํค๋ ๊ธฐ๋ฒ์ ๋๋ค.
1) PyTorch - NVIDIA TensorRT QAT Toolkit ์ ์ฉ ํ์ดํ๋ผ์ธ
NVIDIA์์ ์ ๊ณตํ๋ pytorch-quantization ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ๋ชจ๋ธ์ QAT ๋ชจ๋ธ๋ก ๋ณํํ๋ ํต์ฌ ์ฝ๋ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import pytorch_quantization.nn as quant_nn
from pytorch_quantization import quant_modules
from pytorch_quantization.tensor_quant import QuantDescriptor
# 1. ํ
์ ์์ํ ์ค์ (Symmetric / Per-Channel Quantization ์ค์ )
# Weight: Per-Channel Symmetric, Activation: Per-Tensor Symmetric
quant_modules.initialize()
# 2. ํ์ดํ ์น ๋ชจ๋ธ ์ ์ (Convolution, Linear ๋ ์ด์ด๊ฐ ์๋์ผ๋ก Quantized Module๋ก ๋ํ๋จ)
import torchvision.models as models
model = models.resnet50(pretrained=True)
# 3. Dynamic Range Calibrator๋ฅผ ์ฌ์ฉํ ์ด๊ธฐ Scale ํฉํฐ ์์ง (Warm-up)
with torch.no_grad():
model.eval()
# Enable Calibration Mode
for name, module in model.named_modules():
if isinstance(module, quant_nn.TensorQuantizer):
module.enable_calib()
module.disable_quant()
# ์บ๋ฆฌ๋ธ๋ ์ด์
๋ฐ์ดํฐ ํต๊ณผ (์ฝ 100~500 ๋ฐฐ์น)
for images, _ in calibration_dataloader:
model(images.cuda())
# Calculate scale factor and enable quantization
for name, module in model.named_modules():
if isinstance(module, quant_nn.TensorQuantizer):
module.load_calib_amax()
module.disable_calib()
module.enable_quant()
# 4. QAT Fine-tuning ์ํ
optimizer = torch.optim.Adam(model.parameters(), lr=1e-5) # ๋งค์ฐ ์์ ํ์ต๋ฅ ์ ์ฉ
criterion = torch.nn.CrossEntropyLoss()
for epoch in range(qat_epochs): # ๋ณดํต 1~10 epoch ์ด๋ด ์๋ ด
model.train()
for images, targets in train_dataloader:
optimizer.zero_grad()
outputs = model(images.cuda())
loss = criterion(outputs, targets.cuda())
loss.backward()
optimizer.step()
2) Straight-Through Estimator (STE) ๋ฐ Fine-tuning ์ต์ ํ ํ
์์ํ ํจ์๋ ๋ถ์ฐ์์ ์ด๋ฏ๋ก ๊ณ๋จํ(Step) ํจ์ ํํ๋ฅผ ๊ฐ์ง๋ฉฐ ๋ฏธ๋ถ ๊ฐ์ด 0์ ๋๋ค. ์ด๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด ์ญ์ ํ ์ ๊ธฐ์ธ๊ธฐ(Gradient)๋ฅผ ๊ทธ๋๋ก ํต๊ณผ์ํค๋ STE(Straight-Through Estimator) ๊ธฐ๋ฒ์ ์ฌ์ฉํฉ๋๋ค.
- Learning Rate ์ค์ : ๊ธฐ์กด ํ๋ จ ํ์ต๋ฅ ์ 1/100 ~ 1/1000 ์์ค(์:
1e-5์ดํ)์ผ๋ก ์์ฃผ ๋ฎ๊ฒ ์ค์ ํด์ผ ๊ฐ์ค์น๊ฐ ์์ํ ๊ฒฝ๊ณ๋ฉด์์ ์ง๋ํ๋ ํ์์ ๋ง์ ์ ์์ต๋๋ค. - Batch Normalization ๋๊ฒฐ: QAT ํ์ต ์ด๊ธฐ ๋ช ์ํฌํฌ(Epoch) ํ์๋ Batch Normalization ๋ ์ด์ด์ `running_mean` ๋ฐ `running_var` ์ ๋ฐ์ดํธ๋ฅผ ๋๊ฒฐ(Freeze)ํด์ผ ์ค์ฐจ๊ฐ ์์ ํ๋ฉ๋๋ค.
4. QAT ๊ธฐ๋ฐ ONNX ๋ด๋ณด๋ด๊ธฐ(Export) ๋ฐ TensorRT Engine ๋น๋
QAT ์ฌํ์ต์ด ์๋ฃ๋๋ฉด, ๊ทธ๋ํ ๋ด์ QuantizeLinear ๋ฐ DequantizeLinear (Q/DQ ๋ ธ๋)๊ฐ ํฌํจ๋ ONNX ํ์ผ๋ก ๋ชจ๋ธ์ ๋ด๋ณด๋ด์ผ ํฉ๋๋ค. TensorRT 8.0 ์ด์ ์์ง ๋น๋๋ ์ด Q/DQ ๋ ธ๋๋ฅผ ์ธ์ํ์ฌ ์ต์ ์ INT8 ์ปค๋ ํ์ดํ๋ผ์ธ์ผ๋ก ์ตํฉ(Fusion)ํฉ๋๋ค.
1) ONNX Export ์ ์ ์ ์ฌํญ
ONNX ํ์ผ ๋ณํ ์ opset_version=13 ์ด์์ ์ง์ ํด์ผ Q/DQ ๋
ธ๋๊ฐ ์ ํํ๊ฒ ๋ช
์ธ๋ฉ๋๋ค.
torch.onnx.export(
model,
dummy_input,
"quantized_model.onnx",
opset_version=13,
input_names=["input"],
output_names=["output"]
)
2) TensorRT trtexec๋ฅผ ํตํ Engine ๋น๋
Q/DQ ๋ ธ๋๊ฐ ๋ด์ฅ๋ ONNX๋ ๋ ์ด์ ์ธ๋ถ calibration cache ํ์ผ์ด ํ์ํ์ง ์์ผ๋ฉฐ, TensorRT๊ฐ Q/DQ ์์น๋ฅผ ํ๋จํ์ฌ ์๋์ผ๋ก ๋ ์ด์ด ์ตํฉ(Conv + ReLU + INT8 Scale ๋ฑ)์ ์ํํฉ๋๋ค.
trtexec --onnx=quantized_model.onnx --saveEngine=model_int8.engine --int8
5. ์ค๋ฌด ์ฑ๋ฅ ํ๊ฐ ๋ฐ ๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ ๋น๊ต
์๋ ํ๋ ๋ณต์กํ ๊ฐ์ฒด ์ธ์(Object Detection) ๋ชจ๋ธ์ธ YOLOv8x๋ฅผ ๋์์ผ๋ก ๋์ผํ ํ๋์จ์ด(NVIDIA RTX 4090) ํ๊ฒฝ์์ FP32, PTQ INT8, QAT INT8 ์ฑ๋ฅ์ ํ ์คํธํ ๊ฒฐ๊ณผ ๋น๊ต ์ฌ๋ก์ ๋๋ค.
| ๋ชจ๋ธ ์คํ ์ ๋ฐ๋ | mAP@50:95 (์ ํ๋) | ์ถ๋ก Latency (ms) | GPU VRAM ์ฌ์ฉ๋ |
|---|---|---|---|
| FP32 (Base) | 53.9% | 14.2 ms | 3.8 GB |
| PTQ INT8 (Entropy) | 41.2% (-12.7%) | 4.1 ms | 1.1 GB |
| QAT INT8 (์ต์ ํ ์ ์ฉ) | 53.4% (-0.5%) | 3.9 ms | 1.1 GB |
๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ์์ ๋ณผ ์ ์๋ฏ, PTQ ์ ์ฉ ์ ๋ฐ์ํ๋ **12.7%์ ์น๋ช ์ ์ธ mAP ๋ํญ**์ด QAT ๊ธฐ๋ฒ์ ๋์ ํจ์ผ๋ก์จ **FP32 ๋๋น 0.5% ์ค์ฐจ ๋ฒ์ ๋ด๋ก ์์ ๋ณต๊ตฌ**๋์์ผ๋ฉฐ, INT8 ๋ณธ์ฐ์ ์ฝ 3.6๋ฐฐ ๋น ๋ฅธ ์ฒ๋ฆฌ ์๋(Latency ๋จ์ถ)๋ฅผ ์จ์ ํ ํ๋ณดํ ์ ์์์ต๋๋ค.
๊ฒฐ๋ก : ๋ฅ๋ฌ๋ ํ๋ก๋์ ๋ฐฐํฌ๋ฅผ ์ํ ์์ํ ์ ๋ต ์ต์ ํ ์์ฝ
TensorRT ๊ธฐ๋ฐ์ INT8 ๋ชจ๋ธ ๋ฐฐํฌ ์ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ ์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ๋ฆฌํ ์ ์์ต๋๋ค.
- 1๋จ๊ณ (PTQ ์๋): ๋น ๋ฅธ ๊ฒ์ฆ์ ์ํด
IInt8EntropyCalibrator2๊ธฐ๋ฐ์ PTQ๋ฅผ ๋จผ์ ์ ์ฉํฉ๋๋ค. ์ค์ฐจ๊ฐ 1~2% ๋ฏธ๋ง์ด๋ผ๋ฉด ์ด๋ฅผ ํ๋ก๋์ ์ ์ฆ์ ์ ์ฉํฉ๋๋ค. - 2๋จ๊ณ (์ ๋ฐ ์ง๋จ): ์ ํ๋ ๊ธ๊ฐ์ด ๊ด์ธก๋ ๊ฒฝ์ฐ Polygraphy ๋๊ตฌ๋ก ๋ ์ด์ด๋ณ SQNR์ ๋ชจ๋ํฐ๋งํ์ฌ ์ด์์น(Outlier) ์ ๋ฌด์ ์ค์ผ์ผ ๋ถ์ผ์น ๊ตฌ๊ฐ์ ์๋ณํฉ๋๋ค.
- 3๋จ๊ณ (QAT ์ฌํ์ต ์ ํ): PTQ ํ๊ณ ๊ทน๋ณต์ ์ํด PyTorch TensorRT QAT Toolkit์ ๋์ ํ๊ณ , FakeQuant ๋ ธ๋๋ฅผ ์ฝ์ ํ ํ ๋ฏธ์ธ ์ฌํ์ต(Fine-tuning)์ ๊ฑฐ์ณ Q/DQ ONNX๋ก ๋ด๋ณด๋ ๋๋ค.
- 4๋จ๊ณ (Engine Build): TensorRT 8+ ๋ฒ์ ์์ Q/DQ ๊ทธ๋ํ ์ตํฉ ๊ธฐ๋ฅ์ ํ์ฉํด FP32๊ธ ์ ํ๋์ INT8๊ธ ์ถ๋ก ์๋๋ฅผ ๋ชจ๋ ๋ฌ์ฑํฉ๋๋ค.
์ฑ๊ณต์ ์ธ ๋ชจ๋ธ ์ต์ ํ๋ ๋จ์ํ ์ ๋ฐ๋๋ฅผ ์ค์ด๋ ๊ฒ์ด ์๋๋ผ, ๋คํธ์ํฌ ๊ฐ์ค์น๊ฐ INT8 ํํํ ํ๊ณ ๋ด์์๋ ์๋์ ํํ๋ ฅ์ ์์ง ์๋๋ก QAT ๊ธฐ๋ฐ ํ๋ จ ๋ชจ๋ธ๋ง์ ์ ๊ตํ ์ค๊ณํ๋ ๊ฒ์ ์์ต๋๋ค.