05. INT8 양자화
소형 NPU에서 FP32 가중치는 두 가지 이유로 비쌉니다: 메모리 (4× weight bytes) 와 연산 (FP32 곱셈 = INT8 곱셈 × ~4 area). 양자화는 두 비용을 함께 4× 가깝게 줄입니다.
eulernpu의 양자화는:
- 대칭(symmetric) per-tensor INT8 — 가중치는
scale = max|w| / 127 - post-training (Post-Training Quantization, PTQ)이 기본
- fake-fixed-point sim 으로 양자화 후 정확도를 host에서 검증 가능
- silicon 측정으로 QuantReport 오차 + cycle 영향 모두 측정
QAT (Quantization-Aware Training)는 필요 시 add-on 입니다.
스펙 수준 양자화 표시
quantization:
mode: int8 # fp32 | int8 | int4
policy: symmetric # symmetric (default) | asymmetric
activation_bits: 8
weight_bits: 8
accumulator_bits: 32 # S07: ≥ activation_bits
S07 규칙: accumulator_bits < activation_bits 면 overflow 위험 → validator
가 막습니다.
S23 규칙: quantize / dequantize op이 인접 그래프에서 불일치하면 차단.
양자화 + 컴파일 흐름
# 1) FP32 weights로 컴파일 + golden 생성
eulernpu compile --spec spec.yaml --weights fp32_weights.npz \
--backend cpu_ref --out /tmp/golden
eulernpu run --artifact /tmp/golden --input input.npz --out /tmp/golden.out.npz
# 2) INT8 quantize (per-tensor scale 계산 + 적용)
eulernpu quantize \
--weights fp32_weights.npz \
--calib calib_data.npz \
--out int8_weights.npz \
--report /tmp/quant_report.json
# 3) INT8 compile + run
eulernpu compile --spec spec_int8.yaml --weights int8_weights.npz \
--backend cpu_ref --out /tmp/int8
eulernpu run --artifact /tmp/int8 --input input.npz --out /tmp/int8.out.npz
# 4) FP32 vs INT8 정확도 비교
eulernpu compare --got /tmp/int8.out.npz --expected /tmp/golden.out.npz --tol 0.02
QuantReport 읽기
cat /tmp/quant_report.json
{
"tensors": {
"fc1.weight": {"scale": 0.0127, "max_abs": 1.612, "quant_err_max": 0.0098},
"fc2.weight": {"scale": 0.0089, "max_abs": 1.131, "quant_err_max": 0.0042}
},
"summary": {
"n_tensors": 12,
"max_err": 0.0098,
"median_err": 0.0021
}
}
quant_err_max ≥ 0.05 이면 layer 별 outlier 분석을 권장합니다 (clipping
or per-channel 양자화 검토). CWRU bearing 같은 분포가 좁은 데이터셋은
대개 PTQ 만으로 정확도 손실 없음 (P07 케이스).
fake-fixed-point host sim
silicon 가기 전에 INT8 수학을 host gcc에서 실제 재현:
from eulernpu.quant import FixedPointConfig, quantize_tensor
cfg = FixedPointConfig(bits=8, symmetric=True)
qw, scale = quantize_tensor(fp32_weights, cfg)
# qw: int8, scale: float — 그래프 평가 시 (qw * scale) 로 dequantize
이게 INT8 → FP32 dequantize 결과와 silicon 측에서 ARM이 하는 계산이 byte-equal. host_smoke 가 PASS 하면 silicon 측 수학은 보장됩니다 (silicon-only 함정은 register-map 같은 별도 카테고리).
보드별 INT8 영향
같은 INT8 가중치도 보드별로 cycle/memory 영향이 다름:
eulernpu profile --all-boards /tmp/int8.profile.json
- XC7Z020 — INT8로 weight tile이 BRAM 안에 들어오면 m_axi 비용 사라짐
- XCZU7EV — INT4까지 내려가도 BRAM이 차지 않음 (DSP가 먼저 bottleneck)
INT4도 지원
quantization:
mode: int4
weight_bits: 4
activation_bits: 8
accumulator_bits: 32
S21 규칙: INT4 weight + INT8 activation 조합만 허용 (대칭이 아닌 조합은 차단). ZU-class board (XCZU9EG 등)에서 의미 있음.
다음 단계
다양한 모델 패밀리 (MLP/CNN/Transformer/LSTM/Decoder KV) 예제를 보려면 → 06. models.