실전 사례: Transformer
이전 글에서 우리는 Conv 레이어의 출력 활성화 값을 양자화해봤습니다. 하지만 현대 딥러닝의 주류는 Transformer입니다. BERT, GPT, Vision Transformer 등 거의 모든 최신 모델이 Transformer 아키텍처를 기반으로 합니다.
Transformer는 Conv보다 훨씬 복잡합니다. 여러 종류의 컴포넌트가 서로 다른 특성을 가지고 있고, 각각 다른 양자화 전략이 필요합니다.
Transformer 블록 구조
BERT-base의 한 Transformer 블록을 예시로 사용하겠습니다.
Input (batch=1, seq_len=128, hidden=768)
↓
┌──────────────────────────────────┐
│ Multi-Head Attention │
│ - Query 행렬: (768, 768) │
│ - Key 행렬: (768, 768) │
│ - Value 행렬: (768, 768) │
│ - Attention Scores 계산 │
│ - Output 행렬: (768, 768) │
└──────────────────────────────────┘
↓
Add & Norm (Residual + LayerNorm)
↓
┌──────────────────────────────────┐
│ Feed-Forward Network │
│ - Linear 1: (768, 3072) │
│ - GELU activation │
│ - Linear 2: (3072, 768) │
└──────────────────────────────────┘
↓
Add & Norm (Residual + LayerNorm)
↓
Output (batch=1, seq_len=128, hidden=768)
우리는 이 블록의 각 컴포넌트를 하나씩 양자화 할 것입니다.
Transformer 블록
양자화에 들어가기 전에, Transformer 블록이 정확히 무엇을 계산하는지 수식으로 명확히 이해해야 합니다.
Multi-Head Attention의 수식
입력: $ X \in \mathbb{R}^{seq \times d_{model}} $ (우리 예시: $ 128 \times 768 $)
Step 1: Q, K, V 계산
$$ Q = X W_q, \quad K = X W_k, \quad V = X W_v $$
여기서:
- $ W_q, W_k, W_v \in \mathbb{R}^{768 \times 768} $: 가중치 행렬 ← 양자화 대상 1
- $ Q, K, V \in \mathbb{R}^{128 \times 768} $: 중간 결과
Step 2: Multi-head로 분할
12개 헤드로 나누면 각 헤드는 dimension 64:
$$ Q_i, K_i, V_i \in \mathbb{R}^{128 \times 64}, \quad i = 1, ..., 12 $$
Step 3: Attention Score 계산
각 헤드마다:
$$ \text{Score}_i = \frac{Q_i K_i^T}{\sqrt{d_k}} \in \mathbb{R}^{128 \times 128} $$
여기서:
- $ d_k = 64 $ (head dimension)
- $ \text{Score}_i $: raw attention scores ← 양자화 대상 2 (선택적)
Step 4: Softmax
$$ \text{Attention}_i = \text{softmax}(\text{Score}_i) \in \mathbb{R}^{128 \times 128} $$
여기서:
- : 확률 분포 (각 행의 합 = 1, 값 범위 [0, 1]) ← 양자화 대상 3
Step 5: Value와 곱하기
$$ \text{Head}_i = \text{Attention}_i V_i \in \mathbb{R}^{128 \times 64} $$
Step 6: 헤드 합치기 (Concatenation)
$$ \text{MultiHead} = \text{Concat}(\text{Head}_1, ..., \text{Head}_{12}) \in \mathbb{R}^{128 \times 768} $$
Step 7: Output Projection
$$ \text{Output} = \text{MultiHead} \cdot W_o $$
여기서:
- $ W_o \in \mathbb{R}^{768 \times 768} $: output 가중치 행렬 ← 양자화 대상 4
Feed-Forward Network의 수식
Attention 이후
$$ \text{FFN}(x) = \text{GELU}(x W_1 + b_1) W_2 + b_2 $$
여기서:
- $ W_1 \in \mathbb{R}^{768 \times 3072} $: 첫 번째 가중치 ← 양자화 대상 5
- $ W_2 \in \mathbb{R}^{3072 \times 768} $: 두 번째 가중치 ← 양자화 대상 6
- $ \text{GELU}(x W_1 + b_1) \in \mathbb{R}^{128 \times 3072} $: 중간 활성화 ← 양자화 대상 7
전체 Transformer 블록
$$ \text{Step 1:} \quad Z_1 = \text{LayerNorm}(X + \text{MultiHeadAttention}(X)) $$
$$ \text{Step 2:} \quad Z_2 = \text{LayerNorm}(Z_1 + \text{FFN}(Z_1)) $$
양자화 포인트 정리
가중치 양자화 (모델 크기 감소):
- : Query, Key, Value 가중치
- : Attention output 가중치
- $ W_1, W_2 $: FFN 가중치
활성화 값 양자화 (연산 속도 향상):
- $ Q, K, V $: Query, Key, Value 행렬
- $ \text{Attention}_i $: Softmax 출력 (특별한 케이스: [0,1] 범위)
- $ \text{GELU}(x W_1 + b_1) $: FFN 중간 활성화
이제 각 컴포넌트의 특성을 알았으니, 하나씩 양자화해봅시다. $ W_q $ 가중치 부터 시작합니다.
1. Multi-Head Attention의 QKV 행렬 양자화
시나리오 설정
입력:
- Input embedding: (128, 768) = sequence length 128, hidden dimension 768
- 12개의 attention heads (head_dim = 768/12 = 64)
Query 행렬 계산:
Q = Input × $ W_q $
$ W_q $: (768, 768) 가중치 행렬
우리는 $ W_q $ 가중치를 양자화해보겠습니다.
Step 1: 가중치 분포 분석
$ W_q $ 가중치 통계 (학습된 BERT 모델)
- 가중치 개수: 768 × 768 = 589,824개
- 최소값: $ w_{\min} = -0.087 $
- 최대값: $ w_{\max} = 0.092 $
- 평균: $ \mu = 0.0003 $ (거의 0)
- 표준편차: $ \sigma = 0.028 $
분포 특성:
거의 0 중심으로 대칭적
99%의 가중치가 [-0.08, 0.08] 사이
→ Symmetric quantization이 적합
샘플 가중치들:
[-0.087, -0.062, -0.041, -0.018, 0.000, 0.023, 0.045, 0.071, 0.092]
Step 2: Symmetric Quantization 적용
범위가 대칭적이므로 Symmetric 방식을 사용합니다.
최대 절댓값 찾기
$$ \alpha = \max(|w_{\min}|, |w_{\max}|) = \max(0.087, 0.092) = 0.092 $$
Scale 계산 (Symmetric)
$$ s = \frac{2\alpha}{q_{\max} - q_{\min}} = \frac{2 \times 0.092}{127 - (-128)} = \frac{0.184}{255} = 0.000722 $$
Zero-point (Symmetric)
$$z = 0$$
결과:
- Scale: $ s = 0.000722 $
- Zero-point: $ z = 0 $
- 양자화 수식: $ w_q = \text{round}(w / 0.000722) $
Step 3: 샘플 가중치 양자화
가중치 1: $ w = -0.087 $
$$ w_q = \text{round}(-0.087 / 0.000722) = \text{round}(-120.50) = -121 $$
가중치 2: $ w = -0.062 $
$$ w_q = \text{round}(-0.062 / 0.000722) = \text{round}(-85.87) = -86 $$
가중치 3: $ w = -0.041 $
$$ w_q = \text{round}(-0.041 / 0.000722) = \text{round}(-56.79) = -57 $$
가중치 4: $ w = -0.018 $
$$ w_q = \text{round}(-0.018 / 0.000722) = \text{round}(-24.93) = -25 $$
가중치 5: $ w = 0.000 $
$$ w_q = \text{round}(0.000 / 0.000722) = 0 $$
가중치 6: $ w = 0.023 $
$$ w_q = \text{round}(0.023 / 0.000722) = \text{round}(31.86) = 32 $$
가중치 7: $ w = 0.045 $
$$ w_q = \text{round}(0.045 / 0.000722) = \text{round}(62.33) = 62 $$
가중치 8: $ w = 0.071 $
$$ w_q = \text{round}(0.071 / 0.000722) = \text{round}(98.34) = 98 $$
가중치 9: $ w = 0.092 $
$$ w_q = \text{round}(0.092 / 0.000722) = \text{round}(127.42) = 127 $$
변환 결과:
원본 FP32: [-0.087, -0.062, -0.041, -0.018, 0.000, 0.023, 0.045, 0.071, 0.092]
양자화 INT8: [-121, -86, -57, -25, 0, 32, 62, 98, 127]
Step 4: 역양자화와 오차
Dequantization
$$ \hat{w} = w_q \times s $$
가중치 1: $ \hat{w} = -121 \times 0.000722 = -0.0874 $ (원본: -0.087, 오차: 0.0004)
가중치 2: $ \hat{w} = -86 \times 0.000722 = -0.0621 $ (원본: -0.062, 오차: 0.0001)
가중치 3: $ \hat{w} = -57 \times 0.000722 = -0.0412 $ (원본: -0.041, 오차: 0.0002)
가중치 4: $ \hat{w} = -25 \times 0.000722 = -0.0181 $ (원본: -0.018, 오차: 0.0001)
가중치 5: $ \hat{w} = 0 \times 0.000722 = 0.0000 $ (원본: 0.000, 오차: 0.0000)
가중치 6: $ \hat{w} = 32 \times 0.000722 = 0.0231 $ (원본: 0.023, 오차: 0.0001)
가중치 7: $ \hat{w} = 62 \times 0.000722 = 0.0448 $ (원본: 0.045, 오차: 0.0002)
가중치 8: $ \hat{w} = 98 \times 0.000722 = 0.0708 $ (원본: 0.071, 오차: 0.0002)
가중치 9: $ \hat{w} = 127 \times 0.000722 = 0.0917 $ (원본: 0.092, 오차: 0.0003)
오차 분석:
평균 절대 오차: 0.00018
최대 절대 오차: 0.0004
평균 상대 오차: 0.34%
Key, Value 가중치도 이와 유사하게 양자화를 진행하게 됩니다.
2. Attention Scores 양자화
Multi-Head Attention에서:
$$ \text{Score}_i = \frac{Q_i K_i^T}{\sqrt{d_k}} \in \mathbb{R}^{128 \times 128} $$
$$ \text{Attention}_i = \text{softmax}(\text{Score}_i) \in \mathbb{R}^{128 \times 128} $$
우리는 $ \text{Attention}_i $(Softmax 출력)를 양자화 합니다.
왜 Attention Scores를 양자화하는가?
메모리 측면:
- 12개 헤드 × (128×128) = 196,608개 값
- FP32: 196,608 × 4 bytes = 786 KB
- INT8: 196,608 × 1 byte = 196 KB
- 75% 절감
연산 측면:
- INT8 행렬 곱셈이 FP32보다 훨씬 빠름
Step 1: Softmax 출력의 특성
Softmax 출력은 확률 분포입니다:
수학적 특성:
- 범위 제한: 모든 값이 [0,1][0, 1] 사이
- 합이 1: 각 행(query)의 합 = 1
- 희소성(Sparsity): 몇 개 값만 크고 대부분 0에 가까움
실제 BERT의 Attention 분포 예시:
한 query에 대한 128개 key의 attention 값:
토큰 위치: [0, 1, 2, 3, 4, 5, ..., 127]
Attention: [0.42, 0.38, 0.15, 0.03, 0.01, 0.00, ..., 0.00]
↑ ↑ ↑
주요 토큰들 (나머지는 거의 0)
분포 특성:
- 상위 3~5개 토큰이 전체 attention의 90% 차지
- 나머지 대부분은 0.01 이하
- 매우 비대칭적 분포
Step 2: 샘플 Attention 값 준비
한 헤드의 한 query에 대한 attention 값들 (128개 중 일부):
[0.423, 0.381, 0.152, 0.028, 0.009, 0.004, 0.002, 0.001, 0.000, 0.000]
전체 통계 (한 헤드의 128×128 attention matrix):
- 최소값: $ a_{\min} = 0.0000 $
- 최대값: $ a_{\max} = 0.562 $
- 평균: $ \mu = 0.0078 $ (= 1/128)
- 중앙값: $ 0.0013 $ (대부분 작은 값)
핵심 관찰:
- 범위: [0, 0.562] - 완전히 비대칭!
- 0을 포함 (많은 값이 거의 0)
- Asymmetric quantization 필수
Step 3: Asymmetric Quantization 적용
Scale 계산
$$ s = \frac{a_{\max} - a_{\min}}{q_{\max} - q_{\min}} = \frac{0.562 - 0}{127 - (-128)} = \frac{0.562}{255} = 0.002204 $$
Zero-point 계산
$$ z = q_{\min} - \text{round}\left(\frac{a_{\min}}{s}\right) = -128 - \text{round}\left(\frac{0}{0.002204}\right) = -128 - 0 = -128 $$
결과:
- Scale: s=0.002204s = 0.002204
- Zero-point: z=−128z = -128
- 실수 0 → 정수 -128로 매핑
Step 4: 샘플 값 양자화
양자화 수식
$$ a_q = \text{round}(a/s) + z $$
값 1: $ a = 0.423 $
$$ a_q = \text{round}(0.423 / 0.002204) + (-128) = \text{round}(191.9) - 128 = 192 - 128 = 64 $$
값 2: $ a = 0.381 $
$$ a_q = \text{round}(0.381 / 0.002204) - 128 = \text{round}(172.9) - 128 = 173 - 128 = 45 $$
값 3: $ a = 0.152 $
$$ a_q = \text{round}(0.152 / 0.002204) - 128 = \text{round}(69.0) - 128 = 69 - 128 = -59 $$
값 4: $ a = 0.028 $
$$ a_q = \text{round}(0.028 / 0.002204) - 128 = \text{round}(12.7) - 128 = 13 - 128 = -115 $$
값 5: $ a = 0.009 $
$$ a_q = \text{round}(0.009 / 0.002204) - 128 = \text{round}(4.1) - 128 = 4 - 128 = -124 $$
값 6: $ a = 0.004 $
$$ a_q = \text{round}(0.004 / 0.002204) - 128 = \text{round}(1.8) - 128 = 2 - 128 = -126 $$
값 7: $ a = 0.002 $
$$ a_q = \text{round}(0.002 / 0.002204) - 128 = \text{round}(0.9) - 128 = 1 - 128 = -127 $$
값 8: $ a = 0.001 $
$$ a_q = \text{round}(0.001 / 0.002204) - 128 = \text{round}(0.45) - 128 = 0 - 128 = -128 $$
값 9: $ a = 0.000 $
$$ a_q = \text{round}(0.000 / 0.002204) - 128 = 0 - 128 = -128 $$
값 10: $ a = 0.000 $
$$ a_q = 0 - 128 = -128 $$
변환 결과:
원본 FP32: [0.423, 0.381, 0.152, 0.028, 0.009, 0.004, 0.002, 0.001, 0.000, 0.000]
양자화 INT8: [64, 45, -59, -115, -124, -126, -127, -128, -128, -128]
Step 5: 역양자화와 오차
Dequantization
$$ \hat{a} = (a_q - z) \times s = (a_q + 128) \times 0.002204 $$
값 1: $ \hat{a} = (64 + 128) \times 0.002204 = 192 \times 0.002204 = 0.423 $ (원본: 0.423, 오차: 0.000)
값 2: $ \hat{a} = 173 \times 0.002204 = 0.381 $ (원본: 0.381, 오차: 0.000)
값 3: $ \hat{a} = 69 \times 0.002204 = 0.152 $ (원본: 0.152, 오차: 0.000)
값 4: $ \hat{a} = 13 \times 0.002204 = 0.029 $ (원본: 0.028, 오차: 0.001)
값 5: $ \hat{a} = 4 \times 0.002204 = 0.009 $ (원본: 0.009, 오차: 0.000)
값 6: $ \hat{a} = 2 \times 0.002204 = 0.004 $ (원본: 0.004, 오차: 0.000)
값 7: $ \hat{a} = 1 \times 0.002204 = 0.002 $ (원본: 0.002, 오차: 0.000)
값 8: $ \hat{a} = 0 \times 0.002204 = 0.000 $ (원본: 0.001, 오차: 0.001)
값 9: $ \hat{a} = 0 \times 0.002204 = 0.000 $ (원본: 0.000, 오차: 0.000)
값 10: $ \hat{a} = 0 \times 0.002204 = 0.000 $ (원본: 0.000, 오차: 0.000)
Step 6: 중요한 관찰
1. 작은 값들의 정밀도 문제
0.001, 0.000 → 모두 -128로 양자화 → 0.000으로 복원
작은 attention 값들이 구분되지 않음
2. 하지만 실용적으로는 문제없음
- 작은 attention 값 (< 0.01)은 output에 거의 기여 안 함
- 중요한 큰 값들 (> 0.1)은 정밀하게 보존됨
- 전체 attention의 합 ≈ 1은 여전히 유지
3. 정수 범위 활용
[0, 0.562] 범위를 [-128, 127] 전체에 매핑
정수 범위 완전 활용
Attention 특성과 양자화 전략
왜 Asymmetric인가?
- Softmax 출력은 항상 [0, 1]
- 0을 많이 포함
- Symmetric으로 하면 [-0.562, 0.562]로 확장 → 50% 낭비
정확도 영향
BERT 전체 양자화:
- Attention만 INT8: 정확도 0.1%p 하락
- 가중치 + Attention INT8: 정확도 0.4%p 하락
Attention 양자화는 상대적으로 안전함
3. Feed-Forward Network (FFN) 양자화
Attention 이후 각 위치에 적용되는 FFN
$$ \text{FFN}(x) = \text{GELU}(x W_1 + b_1) W_2 + b_2 $$
여기서:
- $ x \in \mathbb{R}^{768} $: 입력 (한 토큰)
- $ W_1 \in \mathbb{R}^{768 \times 3072} $: 첫 번째 가중치 (확장)
- $ text{GELU}(x W_1 + b_1) \in \mathbb{R}^{3072} $: 중간 활성화
- $ W_2 \in \mathbb{R}^{3072 \times 768} $: 두 번째 가중치 (축소)
배치 처리시:
- 입력: $ (128, 768) $
- $ x W_1 $: $ (128, 3072) $
- GELU 후: $ (128, 3072) $
- 최종 출력: $ (128, 768) $
GELU 활성화 함수란?
GELU (Gaussian Error Linear Unit):
$$ \text{GELU}(x) = x \cdot \Phi(x) = x \cdot \frac{1}{2}\left[1 + \text{erf}\left(\frac{x}{\sqrt{2}}\right)\right] $$
특성:
- ReLU와 달리 음수를 완전히 0으로 만들지 않음
- 음수 입력에 대해 작은 음수 출력 가능
- 대부분의 값은 양수 (0 근처에서 거의 0)
GELU 출력 예시:
입력 x: [-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 3.0]
GELU(x): [-0.05, -0.16, -0.15, 0.00, 0.35, 0.84, 1.96, 3.00]
↑ 작은 음수 ↑ 양수 위주
3-1. W₁ 가중치 양자화 (768 → 3072 확장)
Step 1: 가중치 분포 분석
$ W_1 $ 가중치 통계 (BERT-base 첫 번째 블록)
- 가중치 개수: 768 × 3072 = 2,359,296개
- 최소값: $ w_{\min} = -0.053 $
- 최대값: $ w_{\max} = 0.058 $
- 평균: $ \mu = 0.0001 $ (거의 0)
- 표준편차: $ \sigma = 0.019 $
분포 특성:
0 중심 대칭
범위가 좁음 (약 [-0.05, 0.06])
→ Symmetric quantization 적합
Step 2: Symmetric Quantization
최대 절댓값
$$ \alpha = \max(|w_{\min}|, |w_{\max}|) = \max(0.053, 0.058) = 0.058 $$
Scale 계산
$$ s = \frac{2\alpha}{255} = \frac{2 \times 0.058}{255} = \frac{0.116}{255} = 0.000455 $$
Zero-point
$$ z = 0 $$
샘플 가중치 양자화:
원본: [-0.053, -0.038, -0.021, 0.000, 0.019, 0.035, 0.058]
양자화: [-116, -84, -46, 0, 42, 77, 127]
결과:
- $ W_1 $: $ s = 0.000455 $, $ z = 0 $
- 매우 작은 Scale → 높은 정밀도
3-2. GELU 출력 (중간 활성화) 양자화
이제 가장 흥미로운 부분입니다. GELU 활성화 함수의 출력을 양자화합니다.
Step 1: GELU 출력 분포 분석
실제 추론 결과 (128 토큰 × 3072 차원 = 393,216개 값):
- 최소값: $ a_{\min} = -0.18 $
- 최대값: $ a_{\max} = 12.47 $
- 평균: $ \mu = 1.83 $
- 표준편차: $ \sigma = 2.15 $
분포 특성:
대부분 양수 (약 95%)
음수는 0 근처에만 소량 존재
범위가 매우 비대칭: [-0.18, 12.47]
→ Asymmetric quantization 필수
히스토그램:
범위 [-0.2, 0.0): 2.1% ← 작은 음수
범위 [ 0.0, 2.0): 52.3% ← 가장 많음
범위 [ 2.0, 4.0): 28.7%
범위 [ 4.0, 6.0): 11.2%
범위 [ 6.0, 8.0): 4.3%
범위 [ 8.0,13.0): 1.4% ← 큰 값들
Step 2: Asymmetric Quantization
Scale 계산
$$ s = \frac{a_{\max} - a_{\min}}{q_{\max} - q_{\min}} = \frac{12.47 - (-0.18)}{127 - (-128)} = \frac{12.65}{255} = 0.0496 $$
Zero-point 계산
$$ z = q_{\min} - \text{round}\left(\frac{a_{\min}}{s}\right) = -128 - \text{round}\left(\frac{-0.18}{0.0496}\right) $$
$$ = -128 - \text{round}(-3.63) = -128 - (-4) = -124 $$
결과:
- Scale: $ s = 0.0496 $
- Zero-point: $ z = -124 $
Step 3: 샘플 값 양자화
샘플 GELU 출력 값들
[-0.18, -0.05, 0.00, 0.47, 1.25, 2.83, 5.16, 8.91, 12.47]
값 1: $ a = -0.18 $(최소값)
$$ a_q = \text{round}(-0.18 / 0.0496) + (-124) = \text{round}(-3.63) - 124 = -4 - 124 = -128 $$
값 2: $ a = -0.05 $
$$ a_q = \text{round}(-0.05 / 0.0496) - 124 = \text{round}(-1.01) - 124 = -1 - 124 = -125 $$
값 3: $ a = 0.00 $
$$ a_q = \text{round}(0.00 / 0.0496) - 124 = 0 - 124 = -124 $$
값 4: $ a = 0.47 $
$$ a_q = \text{round}(0.47 / 0.0496) - 124 = \text{round}(9.48) - 124 = 9 - 124 = -115 $$
값 5: $ a = 1.25 $
$$ a_q = \text{round}(1.25 / 0.0496) - 124 = \text{round}(25.20) - 124 = 25 - 124 = -99 $$
값 6: $ a = 2.83 $
$$ a_q = \text{round}(2.83 / 0.0496) - 124 = \text{round}(57.06) - 124 = 57 - 124 = -67 $$
값 7: $ a = 5.16 $
$$ a_q = \text{round}(5.16 / 0.0496) - 124 = \text{round}(104.03) - 124 = 104 - 124 = -20 $$
값 8: $ a = 8.91 $
$$ a_q = \text{round}(8.91 / 0.0496) - 124 = \text{round}(179.64) - 124 = 180 - 124 = 56 $$
값 9: $ a = 12.47 $ (최대값)
$$ a_q = \text{round}(12.47 / 0.0496) - 124 = \text{round}(251.41) - 124 = 251 - 124 = 127 $$
변환 결과:
원본 FP32: [-0.18, -0.05, 0.00, 0.47, 1.25, 2.83, 5.16, 8.91, 12.47]
양자화 INT8: [-128, -125, -124, -115, -99, -67, -20, 56, 127]
Step 4: 역양자화와 오차
값 1: $ \hat{a} = (-128 + 124) \times 0.0496 = -4 \times 0.0496 = -0.198 $ (원본: -0.18, 오차: 0.018)
값 2: $ \hat{a} = -1 \times 0.0496 = -0.050 $ (원본: -0.05, 오차: 0.000)
값 3: $ \hat{a} = 0 \times 0.0496 = 0.000 $ (원본: 0.00, 오차: 0.000)
값 4: $ \hat{a} = 9 \times 0.0496 = 0.446 $ (원본: 0.47, 오차: 0.024)
값 5: $ \hat{a} = 25 \times 0.0496 = 1.240 $ (원본: 1.25, 오차: 0.010)
값 6: $ \hat{a} = 57 \times 0.0496 = 2.827 $ (원본: 2.83, 오차: 0.003)
값 7: $ \hat{a} = 104 \times 0.0496 = 5.158 $ (원본: 5.16, 오차: 0.002)
값 8: $ \hat{a} = 180 \times 0.0496 = 8.928 $ (원본: 8.91, 오차: 0.018)
값 9: $ \hat{a} = 251 \times 0.0496 = 12.450 $ (원본: 12.47, 오차: 0.020)
Step 5: 중요한 통찰
1. Scale이 큰 이유
- GELU 출력 범위가 넓음 (12.65)
- $ s=0.0496 $ (Conv의 0.0286보다 크고, Attention의 0.002보다 훨씬 큼)
- 정밀도는 상대적으로 낮음
2. 하지만 실용적으로 괜찮은 이유
- FFN은 정보 변환 레이어 (정확한 값보다 패턴이 중요)
- 다음 레이어에서 Layer Normalization이 스케일 보정
- 실험 결과: FFN 중간 활성화 양자화는 정확도에 0.2%p만 영향
3. 정수 범위 활용
[-128, 127] 전체 사용
비대칭 범위를 효율적으로 매핑
3-3. W₂ 가중치 양자화 (3072 → 768 축소)
Step 1: 가중치 분포
$ W_2 $ 가중치 통계
- 가중치 개수: 3072 × 768 = 2,359,296개
- 최소값: $ w_{\min} = -0.041 $
- 최대값: $ w_{\max} = 0.045 $
- 평균: $ \mu = 0.0002 $ (거의 0)
- 표준편차: $ \sigma = 0.015 $
분포 특성:
0 중심 대칭
범위: [-0.041, 0.045]
→ Symmetric quantization 적합
과 비교:
$ W_1 $: 범위 [-0.053, 0.058], σ = 0.019
$ W_2 $: 범위 [-0.041, 0.045], σ = 0.015
$ W_2 $가 $ W_1 $보다 범위가 더 좁음
→ 더 작은 Scale → 더 높은 정밀도
왜 $ W_2 $가 더 좁은 분포를 가지는가?
- 은 768 → 3072로 확장합니다. 더 많은 차원으로 정보를 분산시키므로 개별 가중치가 상대적으로 커야 합니다.
- 는 3072 → 768로 축소합니다. 3072개의 뉴런이 768개로 집약되는 과정에서, 각 가중치의 기여도가 평균화되어 개별 값이 더 작아지는 경향이 있습니다.
Step 2: Symmetric Quantization 적용
최대 절댓값 찾기
$$ \alpha = \max(|w_{\min}|, |w_{\max}|) = \max(0.041, 0.045) = 0.045 $$
Scale 계산
$$ s = \frac{2\alpha}{q_{\max} - q_{\min}} = \frac{2 \times 0.045}{255} = \frac{0.090}{255} = 0.000353 $$
Zero-point:
$$ z = 0 $$
과 Scale 비교:
$ W_1 $: s = 0.000455
$ W_2 $: s = 0.000353
$ W_2 $의 Scale이 더 작음
→ 정밀도 더 높음
→ 실수 0.000353마다 정수 1칸 이동
Step 3: 샘플 가중치 양자화
샘플 가중치들:
[-0.041, -0.029, -0.016, -0.007, 0.000, 0.011, 0.024, 0.038, 0.045]
가중치 1: $ w = -0.041 $
$$ w_q = \text{round}(-0.041 / 0.000353) = \text{round}(-116.15) = -116 $$
가중치 2: $ w = -0.029 $
$$ w_q = \text{round}(-0.029 / 0.000353) = \text{round}(-82.15) = -82 $$
가중치 3: $ w = -0.016 $
$$ w_q = \text{round}(-0.016 / 0.000353) = \text{round}(-45.33) = -45 $$
가중치 4: $ w = -0.007 $
$$ w_q = \text{round}(-0.007 / 0.000353) = \text{round}(-19.83) = -20 $$
가중치 5: $ w = 0.000 $
$$ w_q = \text{round}(0.000 / 0.000353) = 0 $$
가중치 6: $ w = 0.011 $
$$ w_q = \text{round}(0.011 / 0.000353) = \text{round}(31.16) = 31 $$
가중치 7: $ w = 0.024 $
$$ w_q = \text{round}(0.024 / 0.000353) = \text{round}(67.99) = 68 $$
가중치 8: $ w = 0.038 $
$$ w_q = \text{round}(0.038 / 0.000353) = \text{round}(107.65) = 108 $$
가중치 9: $ w = 0.045 $
$$ w_q = \text{round}(0.045 / 0.000353) = \text{round}(127.48) = 127 $$
변환 결과:
원본 FP32: [-0.041, -0.029, -0.016, -0.007, 0.000, 0.011, 0.024, 0.038, 0.045]
양자화 INT8: [-116, -82, -45, -20, 0, 31, 68, 108, 127]
Step 4: 역양자화와 오차
Dequantization
$$ \hat{w} = w_q \times s = w_q \times 0.000353 $$
가중치 1: $ \hat{w} = -116 \times 0.000353 = -0.04095 $ (원본: -0.041, 오차: 0.00005)
가중치 2: $ \hat{w} = -82 \times 0.000353 = -0.02895 $ (원본: -0.029, 오차: 0.00005)
가중치 3: $ \hat{w} = -45 \times 0.000353 = -0.01589 $ (원본: -0.016, 오차: 0.00011)
가중치 4: $ \hat{w} = -20 \times 0.000353 = -0.00706 $ (원본: -0.007, 오차: 0.00006)
가중치 5: $ \hat{w} = 0 \times 0.000353 = 0.00000 $ (원본: 0.000, 오차: 0.000)
가중치 6: $ \hat{w} = 31 \times 0.000353 = 0.01094 $ (원본: 0.011, 오차: 0.00006)
가중치 7: $ \hat{w} = 68 \times 0.000353 = 0.02400 $ (원본: 0.024, 오차: 0.000)
가중치 8: $ \hat{w} = 108 \times 0.000353 = 0.03812 $ (원본: 0.038, 오차: 0.00012)
가중치 9: $ \hat{w} = 127 \times 0.000353 = 0.04483 $ (원본: 0.045, 오차: 0.00017)
오차 분석:
평균 절대 오차: 0.000073
최대 절대 오차: 0.000177 (이론적 한계: 0.5 × 0.000353 = 0.000177)
평균 상대 오차: 0.29%
$ W_1 $ 평균 상대 오차: 0.34%
$ W_2 $ 평균 상대 오차: 0.29% → 더 정밀
$ W_1 $과 $ W_2 $의 오차 차이
핵심은 차원 크기가 아니라 레이어의 역할입니다.
$ W_1 $ (768 → 3072): 확장 레이어
- 768차원의 정보를 3072차원으로 펼쳐서 표현
- 각 가중치가 더 많은 책임을 짐
- 개별 가중치 크기가 상대적으로 큼 → 범위 [-0.053, 0.058]
- Scale이 커짐 → 정밀도 낮아짐
$ W_2 $ (3072 → 768): 축소 레이어
- 3072차원의 정보를 768차원으로 집약
- 3072개 뉴런이 평균화되면서 개별 가중치의 영향이 분산됨
- 개별 가중치 크기가 상대적으로 작음 → 범위 [-0.041, 0.045]
- Scale이 작아짐 → 정밀도 높아짐
직관적 비유:
$ W_1 $: 혼자 많은 일을 해야 함 → 개인 기여도 큼 → 가중치 큼
$ W_2 $: 여럿이 함께 일을 나눔 → 개인 기여도 작음 → 가중치 작음
결론
- 차원 크기가 오차를 결정하는 것이 아니라
- 레이어가 정보를 확장하는가 vs 집약하는가가 가중치 분포를 결정
- 가중치 분포의 범위가 Scale을 결정
- Scale이 양자화 오차를 결정
즉, 같은 구조의 다른 모델이라도 확장 비율이 다르면 (예: 768 → 2048) 이 관계가 달라질 수 있습니다.
정리
지금까지 BERT의 Transformer 블록을 처음부터 끝까지 양자화해봤습니다. 단순히 수식을 보는 것이 아니라, 각 컴포넌트의 특성을 이해하고 직접 계산하면서 실전 감각을 익혔습니다.
컴포넌트별 양자화 전략 요약
| 컴포넌트 분포 | 특성 | 양자화 방식 | Scale | Zero-point |
| $ W_q $, $ W_k $, $ W_v $ | 0 중심 대칭 | Symmetric | 0.000722 | 0 |
| Attention scores | [0, 1] 범위 | Asymmetric | 0.002204 | -128 |
| 0 중심 대칭 | Symmetric | 0.000455 | 0 | |
| GELU 출력 | 대부분 양수 | Asymmetric | 0.0496 | -124 |
| 0 중심 대칭 | Symmetric | 0.000353 | 0 |
이 과정에서 중요한 패턴이 보입니다.
가중치는 항상 Symmetric입니다. 학습 과정의 정규화(L2, Xavier 초기화 등)로 인해 가중치는 자연스럽게 0 중심 대칭 분포를 가집니다. Zero-point가 0이므로 계산이 단순하고, 하드웨어 최적화에 유리합니다.
활성화 값은 Asymmetric입니다. Softmax 출력은 [0, 1]로 제한되고, GELU/ReLU 출력은 대부분 양수입니다. 비대칭적인 실제 분포를 그대로 반영해야 정수 범위를 최대한 활용할 수 있습니다.
Scale은 역할을 반영합니다. 가중치의 Scale(0.0003~0.0007)은 활성화 값의 Scale(0.002~0.05)보다 훨씬 작습니다. 이는 가중치가 더 좁은 범위에 집중되어 있어 높은 정밀도로 표현된다는 의미입니다.
'Model Lightening > Model Compression' 카테고리의 다른 글
| AI 모델 양자화 기초 [5] - 실전: Convolutional Layer (0) | 2026.02.16 |
|---|---|
| AI 모델 양자화 기초 [4] - Scale과 Zero-point의 비밀 (0) | 2026.02.16 |
| AI 모델 양자화 기초 [3] - Affine Quantization의 수학적 원리 (0) | 2026.02.15 |
| AI 모델 양자화 기초 [2] - 양자화란 무엇인가? (0) | 2026.02.15 |
| AI 모델 양자화 기초 [1] - 왜 양자화가 필요한가 (0) | 2026.02.15 |