Transformers documentation

양자화 개념

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

양자화 개념

양자화된 숫자는 int8의 경우 256개, int4의 경우 16개와 같이 작은 값 집합 중 하나만 취할 수 있으므로, 모든 양자화 방식은 반올림 오차를 유발합니다. 각 방식은 이러한 오차가 모델의 출력에 해를 끼치지 않도록 하는 방법에서 차이가 납니다. 이 페이지에서는 이러한 차이를 만드는 선택 사항, 즉 숫자를 해당 작은 값 집합에 어떻게 매핑하는지, 어떤 텐서가 양자화되는지, 어떤 데이터 타입이 사용되는지, 그리고 양자화가 언제 수행되는지를 설명합니다.

Transformers에서 양자화에 처음 접하는 경우 개요부터 시작하세요.

값을 더 적은 비트로 매핑

Quantization은 고정밀 값의 범위를 저비트 타입이 표현할 수 있는 작은 정수 집합으로 매핑합니다. int8은 256개의 정수, [-128, 127]를 표현할 수 있으며, int4는 16개를 표현할 수 있습니다.

Affine quantization은 가장 일반적인 매핑 방식입니다. 텐서의 최소값과 최대값을 가져와 정수 범위를 해당 구간에 걸쳐 확장합니다. 아래 예시에서는 값이 -1.8에서 2.4까지인 텐서를 int8로 매핑하며, 0.0은 -19에 위치합니다.

bf16  -1.8             0.0                    2.4
       ├────────────────┼──────────────────────┤
       ▼                ▼                      ▼
int8  -128             -19                    127

두 개의 매개변수가 매핑을 정의합니다.

  • S 는 S 원래 단위에서 정수 단계 하나의 크기입니다. S=valmax−valminqmax−qmin S = \frac{val_{max} - val_{min}}{q_{max} - q_{min}}

  • zero point Z 는 0.0. Z=qmin−round(valminS) Z = q_{min} - round\left(\frac{val_{min}}{S}\right)

값 x 은(는) scale로 나누고 zero point만큼 이동시켜 정수 q 로 양자화됩니다. q=round(xS+Z) q = round\left(\frac{x}{S} + Z\right)

Dequantizing은 매핑을 역으로 적용하여 x. 의 근사값을 반환합니다. 와 그 근사값 사이의 차이는 반올림 오차입니다. x x≈S⋅(q−Z) x \approx S \cdot (q - Z)

위 예시에서 S는 약 0.0165이고 Z는 -19입니다. 값 0.5는 11로 양자화되고, 11은 약 0.494로 역양자화되어 약 0.006의 반올림 오차가 발생합니다.

┌─────┐  quantize   ┌────┐ dequantize   ┌───────┐
│ 0.5 │ ──────────▶ │ 11 │ ───────────▶ │ 0.494 │
└─────┘             └────┘              └───────┘

Affine 양자화는 두 가지 형태로 제공됩니다.

  • Asymmetric 양자화는 위의 수식을 사용합니다. 정확한 범위를 매핑하므로, 가중치가 0을 중심으로 정렬되지 않은 경우에도 표현 가능한 값이 낭비되지 않습니다.
  • Symmetric 양자화는 범위를 [-a, a]로 가정하고 Z를 0으로 고정하므로, 스케일만 저장됩니다. 계산 비용은 낮아지지만, 가중치가 0의 한쪽으로 치우쳐 있으면 표현 가능한 값 중 일부가 사용되지 않습니다.

granularity 선택

tensor 전체에 하나의 scale만 사용하는 것은 저장 비용이 적게 들지만, 하나의 큰 outlier가 범위를 넓혀 나머지 모든 weight의 표현 가능한 값 간격을 더 넓게 벌립니다. 더 작은 그룹으로 나누면 각 scale이 커버해야 할 범위가 더 좁아집니다.

  • Per-tensor 방식은 전체 텐서에 대해 하나의 scale과 하나의 zero point를 사용합니다.
  • Per-channel은 각 출력 채널, 즉 가중치 행렬의 각 행에 대해 하나의 스케일을 사용합니다.
  • Per-group 또는 per-block 방식은 128개의 가중치와 같은 연속된 값의 각 그룹에 대해 하나의 scale을 사용합니다. 각 그룹은 해당 scale을 위한 추가 저장 공간을 필요로 합니다.

아래 다이어그램은 3행의 가중치 행렬에 대한 세 가지 옵션을 보여줍니다. 각 s는 하나의 스케일을 나타냅니다.

per-tensor        per-channel           per-group
┌────────────┐    ┌────────────┐        ┌──────┬──────┐
│            │    │            │ s0     │  s0  │  s1  │
│            │    ├────────────┤        ├──────┼──────┤
│     s      │    │            │ s1     │  s2  │  s3  │
│            │    ├────────────┤        ├──────┼──────┤
│            │    │            │ s2     │  s4  │  s5  │
└────────────┘    └────────────┘        └──────┴──────┘

대부분의 저비트 방법은 그룹 단위로 양자화를 수행합니다. GPTQ의 기본값은 128개 가중치 그룹이며, Fine-grained FP8의 기본값은 128x128 블록입니다. Data types의 FP4 형식은 블록 크기를 형식 자체에 내장합니다.

가중치 또는 활성화 값 양자화

Methods는 양자화 대상 텐서가 서로 다르며, 이는 행렬 곱셈이 어디서 실행되는지를 결정합니다.

  • Weight-only 방식은 가중치를 저정밀도로 저장하고, 활성화 값은 bf16 또는 fp16으로 유지합니다. 각 matmul을 수행하기 전에 커널은 가중치를 더 높은 정밀도의 compute dtype으로 dequantize합니다. bitsandbytes 4-bit, AWQ, GPTQ, HQQ는 이 방식으로 동작합니다. bitsandbytes에서는 bnb_4bit_compute_dtype이 해당 compute dtype을 설정합니다.
  • Weight-and-activation 방식은 활성화 값도 양자화하며, matmul을 저정밀도로 직접 실행합니다. 이를 위해서는 최신 GPU의 FP8과 같은 저정밀도 matmul을 지원하는 하드웨어가 필요합니다. 이 방식은 8비트 가중치와 활성화를 의미하는 W8A8으로 표기되는 경우가 많습니다.

Weight-only 방식은 메모리를 절약하지만, dequantizing가 모든 matmul에 추가 작업을 발생시키므로 항상 더 빠르게 실행되지는 않습니다. 모델이 memory-bound인 경우 생성 속도가 빨라집니다. 토큰을 하나씩 생성할 때 GPU는 각 단계에서 연산보다 메모리에서 가중치를 읽는 데 대부분의 시간을 사용합니다. 4-bit 가중치를 읽으면 bf16 가중치 대비 데이터 이동량이 4분의 1로 줄어들며, 이 절감 효과가 dequantizing 비용을 상쇄할 수 있습니다.

Weight-and-activation 방식은 prefill이나 큰 배치와 같이 모델이 compute-bound인 상황에서 도움이 됩니다. 이는 저정밀도 matmul 자체가 더 빠르기 때문입니다. 일부 방식은 두 방식 사이를 전환합니다. NVFP4는 prefill에는 4-bit-weight, 4-bit-activation matmul을, decoding에는 4-bit-weight, 16-bit-activation matmul을 실행합니다.

데이터 유형

데이터 유형은 양자화된 숫자가 표현할 수 있는 값을 결정합니다.

int8 및 int4

int8과 int4는 균등 간격의 정수를 나타내며, 위의 affine mapping을 사용합니다. int4는 16개의 값만 표현할 수 있으므로 int8보다 정확도가 더 많이 손실됩니다. int4를 사용하는 방법은 일반적으로 그룹별 양자화 또는 보정(calibration)을 적용하여 정확도를 유지합니다.

대부분의 하드웨어에는 4비트 저장 형식이 없으므로, 두 개의 int4 값을 패킹하여 하나의 바이트에 저장합니다. 예를 들어, 첫 번째 값은 하위 4비트를, 두 번째 값은 상위 4비트를 사용할 수 있습니다. 커널은 연산을 수행하기 전에 값을 언패킹합니다.

┌──────┐  ┌──────┐        ┌──────┬──────┐
│ val1 │  │ val2 │        │ val2 │ val1 │
│ 0101 │  │ 0011 │   →    │ 0011 │ 0101 │  1 byte
└──────┘  └──────┘        └──────┴──────┘
  int4      int4            high   low

4-bit 타입 중 일부는 가중치의 분포에 맞춰 표현 가능한 값의 간격을 균일하지 않게 배치합니다. bitsandbytes의 NF4는 대부분의 가중치가 위치한 0 근처에 더 많은 값을 배치합니다.

FP8

FP8은 부호, 지수, 가산부를 8비트로 구성하는 부동 소수점 레이아웃을 유지합니다. 표현 가능한 값은 0 근처에서 더 밀집하고 큰 절댓값에서는 더 넓게 간격을 두며, 이는 가중치와 활성화 값의 분포 특성에 적합합니다.

FP8은 두 가지 변형이 있습니다.

  • E4M3는 지수 비트 4개와 맨티사 비트 3개를 가집니다. 더 높은 정밀도를 제공하지만 범위는 더 좁습니다. 추론 시 가중치와 활성화 값에 일반적으로 선택되는 포맷입니다.
  • E5M2는 지수 비트 5개와 맨티사 비트 2개를 가집니다. 정밀도는 낮지만 더 넓은 범위를 커버합니다.
E4M3   ┌─┬───────┬─────┐
       │S│E E E E│M M M│
       └─┴───────┴─────┘
E5M2   ┌─┬─────────┬───┐
       │S│E E E E E│M M│
       └─┴─────────┴───┘
  S sign   E exponent   M mantissa

FP8 matmul은 하드웨어 지원이 필요합니다. NVIDIA GPU(Compute Capability 8.9 이상, Ada, Hopper, Blackwell), AMD Instinct MI300, Intel XPU 등이 해당됩니다. Transformers는 Fine-grained FP8, FBGEMM FP8, compressed-tensors를 통해 FP8을 지원합니다.

블록 스케일과 함께 FP4

FP4는 부호 비트 1개, 지수 비트 2개, 가수 비트 1개(E2M1)로 구성됩니다. 양수와 음수를 포함하여 0, 0.5, 1, 1.5, 2, 3, 4, 6만 표현할 수 있습니다. 표현 가능한 값이 이렇게 적기 때문에 FP4 포맷은 텐서당 하나의 스케일만으로는 동작할 수 없습니다. 따라서 값의 작은 블록마다 스케일을 연결하고, 이 스케일들을 포맷의 일부로 저장합니다.

형식블록 크기블록별 스케일추가 스케일
MXFP432개의 값8비트 2의 거듭제곱(E8M0)None
NVFP416개의 값FP8텐서당 FP32 스케일 1개

MXFP4는 microscaling 형식입니다. 2의 거듭제곱 스케일은 지수만 이동시키므로 적용 비용이 낮지만, 2의 거듭제곱 값에만 적용될 수 있습니다. NVFP4는 더 작은 블록과 2의 거듭제곱 사이의 값을 가질 수 있는 FP8 스케일을 사용합니다. 이를 통해 더 높은 정확도를 유지할 수 있지만, FP8 블록 스케일의 범위를 유지하기 위해 텐서별(per-tensor) 스케일이 필요합니다. Four Over Six와 FP-Quant도 NVFP4 또는 MXFP4 가중치를 생성합니다.

같은 블록 개념은 8비트에서도 나타납니다. MXFP8은 2의 거듭제곱 블록 스케일을 사용하여 FP8 값을 저장하며, Fine-grained FP8가 이를 로드합니다.

코드북

4bit 미만에서는 일부 방법이 각 가중치를 가장 가까운 표현 가능한 값으로 반올림하는 방식을 중단합니다. 이러한 방법은 가중치 그룹을 벡터로 함께 양자화합니다. 코드북(codebook)에는 학습된 벡터 집합이 포함되며, 각 가중치 그룹은 가장 가까운 벡터의 인덱스로 저장됩니다. AQLM의 기본 설정은 8개 가중치 그룹과 16bit 인덱스이며, 이는 가중치당 2bit에 해당합니다. VPTQ도 코드북을 사용합니다. 좋은 코드북을 찾는 데에는 교정 데이터에 대한 긴 최적화가 필요하므로, 이러한 모델은 사전에 양자화되어 체크포인트로 로드됩니다.

양자화가 발생하는 시점

모델은 학습 후 또는 학습 중에 양자화됩니다. Transformers의 대부분의 방법은 학습 후 양자화를 수행합니다.

학습 후 양자화

Post-training quantization (PTQ)는 이미 학습이 완료된 모델을 학습된 내용을 변경하지 않고 양자화합니다. PTQ 방법은 데이터가 필요한지에 따라 차이가 있으며, 이는 문서의 목차에서 해당 방법이 어느 그룹에 속하는지를 결정합니다.

  • 교정 데이터가 없으면 스케일은 가중치만으로 결정되며, 모델 로딩 중에 실행할 수 있을 정도로 양자화가 빠릅니다. bitsandbytes, HQQ, torchao가 이 방식으로 동작합니다. 이러한 방법은 로딩 중 양자화 섹션에 나열되어 있습니다.
  • 교정 데이터를 사용하는 경우, 이 방법은 작은 데이터셋을 모델에 통과시켜 활성화 값을 기반으로 각 레이어를 어떻게 양자화할지 결정합니다. 교정은 4비트 이하에서 일반적으로 더 나은 정확도를 제공하지만, 수 분에서 수 시간까지 소요됩니다.

Calibration methods는 활성화(activation)를 다양한 방식으로 활용합니다. GPTQ는 레이어를 한 열(column)씩 양자화하고, 각 열의 반올림 오차를 보정하기 위해 나머지 열을 조정합니다. AWQ는 활성화가 가장 큰 가중치 채널을 찾아 반올림하기 전에 스케일링하여 이를 보호합니다.

calibration runs의 실행 방식에 따라 Transformers에서 해당 메서드를 사용하는 방법이 결정됩니다.

  • dataset을 GPTQConfig에 전달하면 GPTQ는 from_pretrained 내에서 캘리브레이션을 수행하므로, 로딩 시 양자화 항목에 나열됩니다.
  • AWQ, AutoRound, AQLM, VPTQ와 같은 다른 보정 방법은 각자의 라이브러리에서 보정을 수행합니다. Transformers는 그 결과를 로드하므로, 이러한 방법은 사전 양자화 체크포인트 로드 섹션에 나열되어 있습니다.

양자화 인식 학습

양자화 인식 학습(QAT)은 학습 중에 양자화를 시뮬레이션합니다. 순전파(forward pass)에서는 양자화처럼 값을 반올림하고, 그라디언트는 전체 정밀도(full-precision) 가중치를 업데이트하므로 모델이 반올림 오차를 견디도록 학습됩니다. QAT는 매우 낮은 비트 폭에서 일반적으로 가장 높은 정확도를 제공하지만, 모델을 학습시켜야 합니다.

BitNet 모델은 루프 내에서 1.58-bit 가중치를 사용하여 학습되며, Transformers는 학습된 체크포인트를 로드합니다.

리소스

Update on GitHub