저장만으로는 부족하다

지난 글에서 K-Means 기반 양자화를 봤다. 가중치를 몇 개의 대표값으로 묶어 모델 크기를 크게 줄였지만, 결정적인 한계가 있었다. 줄어드는 건 저장 용량뿐이고, 추론할 때는 코드북으로 디코드해서 여전히 floating-point 연산을 한다는 점이다.

이번엔 그 벽을 넘는다. 가중치를 정수로 저장하는 것을 넘어, 곱셈과 덧셈까지 전부 정수 연산으로 추론하는 방법 — Linear Quantization(선형 양자화) 이다. 2018년 Jacob et al.의 논문1이 제안했고, TensorFlow Lite의 INT8 양자화가 바로 이 방식이다.


핵심: 정수와 실수를 잇는 아핀 매핑

Linear Quantization은 정수를 실수로 보내는 아핀(affine) 매핑이다. 딱 한 줄이다.

$$r = S \cdot (q - Z)$$
  • $r$ — 원래의 실수(real) 값 (floating-point)
  • $q$ — 양자화된 정수(quantized integer)
  • $Z$ — zero point. 실수 $0$에 정확히 대응하는 정수다. (정수)
  • $S$ — scale. 정수 한 칸이 실수로 얼마만큼인지. (floating-point)

즉 정수 $q$에서 zero point $Z$를 빼고 scale $S$를 곱하면 원래 실수가 복원된다. 반대로 실수를 정수로 만들 때는 $q = \text{round}(r / S + Z)$로 반올림한다.

$Z$가 따로 있는 이유가 중요하다. 실수 $0$(예: ReLU 뒤의 0, 패딩)은 신경망에서 아주 흔하고 정확히 표현돼야 한다. zero point는 실수 0이 어떤 정수에도 오차 없이 딱 맞아떨어지도록 보장하는 장치다.

아래 위젯에서 직접 확인해보자. 실수 가중치 행렬이 정수 $q$로 양자화되고, 다시 $S(q-Z)$로 복원된다. bits를 바꾸면 정수 범위가, asymmetric/symmetric을 누르면 $Z$의 처리 방식이 달라지고, 아래에 scale $S$, zero point $Z$, 양자화 오차가 실시간으로 갱신된다.


S와 Z는 어떻게 정하나

$N$비트 정수의 범위는 2의 보수 기준으로 정해진다.

비트 폭$q_{\min}$$q_{\max}$
2−21
3−43
4−87
$N$$-2^{N-1}$$2^{N-1}-1$

이제 실수 범위 $[r_{\min}, r_{\max}]$의 양 끝이 정수 범위의 양 끝에 대응한다고 놓자.

$$r_{\max} = S(q_{\max} - Z), \qquad r_{\min} = S(q_{\min} - Z)$$

두 식을 빼면 $Z$가 사라지고 scale이 나온다.

$$S = \frac{r_{\max} - r_{\min}}{q_{\max} - q_{\min}}$$

위젯 기본값(2비트, 실수 범위 $[-1.08, 2.12]$)로 계산하면 $S = \frac{2.12 - (-1.08)}{1 - (-2)} = \frac{3.20}{3} \approx 1.07$ 이다.

zero point는 아래 식에서 나온다. $r_{\min} = S(q_{\min} - Z)$를 $Z$에 대해 풀고, 정수여야 하니 반올림한다.

$$Z = \text{round}\left(q_{\min} - \frac{r_{\min}}{S}\right)$$

같은 예제로 $Z = \text{round}\left(-2 - \frac{-1.08}{1.07}\right) = \text{round}(-0.99) = -1$ 이다. 위젯의 stats에 나오는 $S$, $Z$와 맞는지 확인해보자.


진짜 핵심: 정수 행렬곱

여기까지는 “정수로 저장하기"다. 이제 연산도 정수로 하는 부분을 보자. 신경망의 기본 연산인 행렬곱 $Y = WX$를 생각한다. 각 값을 아핀 매핑으로 바꾸면:

$$S_Y(q_Y - Z_Y) = S_W(q_W - Z_W)\cdot S_X(q_X - Z_X)$$

$q_Y$에 대해 정리하면:

$$q_Y = \frac{S_W S_X}{S_Y}\big(q_W q_X - Z_W q_X - Z_X q_W + Z_W Z_X\big) + Z_Y$$

괄호 안을 보자. $q_W q_X$는 정수 곱셈이고, 나머지 $Z_W q_X$, $Z_X q_W$, $Z_W Z_X$ 항은 전부 정수끼리의 곱·합이다. 게다가 $Z_W Z_X$처럼 입력과 무관한 항은 미리 계산(precompute) 해둘 수 있다. 즉 괄호 전체가 정수 산술로 끝난다.

남는 건 앞의 $\frac{S_W S_X}{S_Y}$ 뿐인데, 이건 floating-point다. 여기가 이 방법의 묘수다. 경험적으로 이 스케일 비는 항상 $(0, 1)$ 구간에 있어서, 이렇게 쓸 수 있다.

$$\frac{S_W S_X}{S_Y} = 2^{-n} M_0, \qquad M_0 \in [0.5, 1)$$

$M_0$는 고정소수점(fixed-point) 곱셈으로, $2^{-n}$은 그냥 비트 시프트(bit shift) 로 처리된다. 결국 floating-point 유닛이 전혀 필요 없다. 모든 연산이 정수 곱셈·덧셈·시프트로 끝난다.


더 단순하게: 대칭 양자화와 bias 접기

실전에서는 두 가지 단순화를 쓴다.

대칭(symmetric) 양자화 — 가중치 분포는 대개 0을 중심으로 대칭이다. 그래서 가중치의 zero point를 아예 $Z_W = 0$으로 고정한다. 그러면 위 식에서 $Z_W q_X$와 $Z_W Z_X$ 항이 사라져 훨씬 깔끔해진다.

$$q_Y = \frac{S_W S_X}{S_Y}\big(q_W q_X - Z_X q_W\big) + Z_Y$$

bias 접기 — 편향이 있는 $Y = WX + b$에서는 $Z_b = 0$, $S_b = S_W S_X$로 두면 편향까지 자연스럽게 흡수된다. 입력과 무관한 부분($q_b - Z_X q_W$)을 하나의 $q_{bias}$로 미리 합쳐두면:

$$q_Y = \frac{S_W S_X}{S_Y}\big(q_W q_X + q_{bias}\big) + Z_Y$$

합성곱(convolution) 층도 똑같은 구조다. $q_W q_X$ 자리에 $\text{Conv}(q_W, q_X)$가 들어갈 뿐이다. 정리하면 정수 추론 파이프라인은 이렇게 된다.

  1. 정수 입력·가중치로 정수 MAC(곱셈-누산) → int32 누산
  2. 미리 계산한 $q_{bias}$를 정수 덧셈
  3. $\frac{S_W S_X}{S_Y}$를 고정소수점 곱 + 시프트로 rescale → N비트 정수로
  4. zero point $Z_Y$를 정수 덧셈 → 정수 출력

floating-point는 어디에도 없다.


결과: 얼마나 잃고 얼마나 빨라지나

이렇게 INT8로 양자화해도 정확도는 거의 유지된다.

모델Float 정확도INT8 정확도
ResNet-5076.4%74.9%
Inception-V378.4%75.4%

1–3%p 안팎의 손실로 모델은 1/4 크기, 연산은 정수가 된다. 모바일(Snapdragon)에서 float 대비 정수 전용 추론은 같은 정확도에서 훨씬 낮은 지연시간을 보인다 — 정수 유닛이 더 싸고 빠르기 때문이다. (1편에서 본 “저비트 정수 연산이 float보다 수십 배 싸다"는 이야기가 여기서 현실이 된다.)


정리

  • 아핀 매핑 $r = S(q - Z)$ 하나로 정수 $q$와 실수 $r$을 잇는다. $Z$(zero point)는 실수 0을 정수에 오차 없이 맞추는 장치, $S$(scale)는 한 칸의 크기.
  • $S = \frac{r_{\max}-r_{\min}}{q_{\max}-q_{\min}}$, $Z = \text{round}(q_{\min} - r_{\min}/S)$.
  • 정수 행렬곱: $q_W q_X$와 precompute 항으로 괄호가 정수 산술, 앞의 스케일 비는 고정소수점 곱 + 시프트. → floating-point 없이 추론.
  • 대칭 양자화($Z_W=0$)와 bias 접기로 식이 더 단순해진다.

가장 큰 차이는 여기다. K-Means 양자화는 저장만 정수였다(연산은 float). Linear Quantization은 저장도 정수, 연산도 정수다.

방식저장연산
원본FP 가중치FP 산술
K-Means 양자화정수 인덱스 + FP 코드북FP 산술
Linear 양자화정수 가중치정수 산술

이걸로 신경망 양자화의 두 큰 갈래 — 저장을 줄이는 K-Means, 연산까지 정수화하는 Linear — 를 모두 봤다. 다음은 극단으로 밀어붙이는 이야기, 가중치를 $+1/-1$ 한 비트로 줄이는 Binary·Ternary 양자화다.


  1. Jacob et al. Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference. CVPR 2018. ↩︎