Hugging Face, LLM.int8() 8비트 행렬 곱셈을 Transformers와 Accelerate에 통합

TL;DR

Hugging Face는 8‑bit LLM.int8() 양자화 방법이 이제 transformersaccelerate 라이브러리에 완전히 통합되었으며, BLOOM‑176B와 같은 거대한 모델을 대략 절반 수준의 메모리 사용량으로 추론할 수 있게 되었고 정확도 손실은 측정되지 않았다고 발표했습니다.


왜 대형 언어 모델에 8‑bit 양자화가 중요한가

대형 언어 모델(LLM)은 현재 수천억 개의 파라미터를 초과합니다(예: PaLM 540B, OPT 176B, BLOOM 176B). 전체 정밀도 FP32로 모델을 저장하려면 가중치당 4 바이트가 필요해 수백 기가바이트의 메모리가 요구되며, 이는 대부분의 GPU 용량을 훨씬 초과합니다. 반정밀도(FP16/BF16)로 정밀도를 낮추면 메모리가 절반으로 줄지만, 여전히 BLOOM 176B는 약 350 GB가 필요합니다. 8‑bit 정수(INT8) 양자화를 사용하면 추가로 2배 감소가 가능하지만, 전통적인 양자화는 특히 6 B 파라미터를 초과하는 모델에서 정확도가 크게 떨어지는 문제가 있었습니다.

LLM.int8()의 핵심 아이디어: 정확도 손실 없는 행렬 곱셈

LLM.int8()는 아웃라이어 값을 별도로 처리함으로써 정확도 저하를 방지합니다:

  1. 아웃라이어 추출 – 크기가 임계값(≈6)보다 큰 값들을 은닉 상태 행렬의 각 열마다 식별합니다.
  2. 혼합 정밀도 행렬 곱 – 아웃라이어는 FP16으로 곱하고, 나머지 대부분의 행렬은 INT8로 양자화하여 벡터‑단위(활성화는 행‑단위, 가중치는 열‑단위) 양자화로 곱합니다.
  3. 역양자화 및 합산 – INT8 결과를 FP16으로 역양자화한 뒤 아웃라이어 FP16 결과와 합산해 최종 FP16 출력을 얻습니다. 이 세 단계 과정은 원래 FP16/BF16 모델과 동일한 추론 품질을 유지하면서 메모리 사용량을 1/4로 줄입니다.

양자화 메커니즘: 제로‑포인트 vs. 절대최대값

  • 제로‑포인트 양자화 – 부동소수점 범위(예: [-1, 1])를 INT8 범위 [-127, 127]에 맞춰 스케일링하고 각 값을 반올림합니다. 역스케일링을 통해 원래 값의 근사치를 복원합니다.
  • 절대최대값 양자화 – 각 텐서를 그 절대값 최대값으로 나눈 뒤 127을 곱하고 반올림합니다. 예를 들어 벡터 [1.2, ‑0.5, ‑4.3, …, 5.4]의 경우 스케일링 팩터는 127/5.4 ≈ 23.5가 되어 [-127, 127] 정수값을 얻습니다. 두 방식 모두 행‑단위 또는 열‑단위로 적용할 수 있으며, 이는 대규모 행렬 곱에서 정확성을 확보하는 데 필수적입니다.

제로 정확도 저하에 대한 실증적 증거

lm‑eval‑harness를 사용한 OPT‑175B와 BLOOM‑176B 벤치마크 결과, INT8와 FP16/BF16 점수 간 절대 차이는 모든 작업에서 표준 오차 이하였습니다(예: HellaSwag 정확도 0.7849 vs. 0.7849, Lambada 퍼플렉시티 3.0142 vs. 3.0152). 한 경우(BLOOM‑176B의 Lambada)에서는 INT8 모델이 약간 더 좋은 성능을 보였습니다. 논문 LLM.int8(): 8‑bit Matrix Multiplication for Transformers at Scale에서 전체 평가를 확인할 수 있습니다.

속도 트레이드‑오프

메모리 절감은 가장 큰 모델에서는 약간의 속도 저하를 동반합니다: BLOOM‑176B는 INT8에서 FP16 대비 15 %–23 % 느리게 실행됩니다. 작은 모델(e.g., T5‑3B, T5‑11B)은 초기에는 더 큰 속도 저하를 겪었지만, 최근 최적화로 토큰당 지연 시간이 T5‑3B는 312 ms에서 173 ms로, T5‑11B는 45 ms에서 25 ms로 감소했습니다. 향후 릴리즈에서는 이 격차를 더욱 줄이는 것이 목표입니다.

모델 정밀도 GPU 토큰 / ms (배치 1)
BLOOM‑176B BF16 8 × A100 80GB 239
BLOOM‑176B INT8 4 × A100 80GB 282
T5‑11B FP16 2 × T4 15GB 11.7
T5‑11B INT8 1 × T4 15GB 43.5

transformers에의 통합

핵심 구성 요소는 bitsandbytes.nn.Linear8bitLt이며, 이는 torch.nn.Linear를 대체하는 드롭‑인 구현입니다. 최소 변환 워크플로는 다음과 같습니다:

import torch, bitsandbytes as bnb
from bnb.nn import Linear8bitLt

# FP16 모델 정의 및 가중치 저장
fp16 = torch.nn.Sequential(torch.nn.Linear(64, 64), torch.nn.Linear(64, 64))
torch.save(fp16.state_dict(), "model.pt")

# INT8 버전 구축
int8 = torch.nn.Sequential(
    Linear8bitLt(64, 64, has_fp16_weights=False),
    Linear8bitLt(64, 64, has_fp16_weights=False),
)
int8.load_state_dict(torch.load("model.pt"))
int8 = int8.to(0)   # 양자화가 GPU에서 수행됨

.to 호출 이후 가중치는 [-127, 127] 범위의 int8 텐서로 저장됩니다. 원래 FP16 값은 (weight.CB * weight.SCB) / 127을 통해 복원할 수 있습니다.

accelerate를 활용한 제로‑메모리 모델 구축

accelerate.init_empty_weights()meta 디바이스에 모델을 생성해 RAM을 전혀 할당하지 않습니다. 통합 패치는 accelerate가 파라미터를 meta 디바이스에서 이동할 때 사용자 정의 클래스(Int8Params)를 유지하도록 합니다. 재귀 헬퍼는 모든 nn.LinearLinear8bitLt로 교체하면서 lm_head와 같이 전체 정밀도로 유지해야 하는 모듈은 그대로 둡니다:

from accelerate import init_empty_weights
import torch.nn as nn, bitsandbytes as bnb

def replace_8bit_linear(model, threshold=6.0, exclude="lm_head"):
    for name, module in model.named_children():
        if list(module.children()):
            replace_8bit_linear(module, threshold, exclude)
        if isinstance(module, nn.Linear) and name != exclude:
            with init_empty_weights():
                model._modules[name] = bnb.nn.Linear8bitLt(
                    module.in_features,
                    module.out_features,
                    module.bias is not None,
                    has_fp16_weights=False,
                    threshold=threshold,
                )
    return model

두 개의 PR이 accelerate에 추가되어 set_module_tensor_to_device가 각 INT8 텐서당 정확히 한 번만 호출되도록 하여 이중 양자화 버그를 방지합니다.

하드웨어 및 설치 요구사항

  • GPU 지원 – INT8 텐서 코어가 필요합니다(NVIDIA Turing, Ampere, RTX 20/30, A40‑A100, T4). CPU와 구형 Kepler GPU는 네이티브 지원이 없습니다.
  • 설치 – Python ≥ 3.8:
pip install accelerate bitsandbytes
pip install git+https://github.com/huggingface/transformers.git

시연 예시

Google Colab 노트북에서는 T5‑11B(원래 FP32 기준 42 GB)를 INT8로 단 11 GB만 사용해 실행하는 예시와, BLOOM‑3B를 단일 T4에 맞게 실행하는 데모를 제공합니다.

향후 작업 및 제한 사항

  • 소형 모델 속도 – ≤6 B 모델에 대해 INT8 지연 시간을 FP16 수준으로 맞추는 작업이 진행 중입니다.
  • Kepler GPU 지원 – 네이티브 INT8 텐서 코어가 없는 GPU(GTX 1080 등)를 위한 별도 소프트웨어 스택을 추가할 계획입니다.
  • State‑dict 지속성 – 현재 INT8 체크포인트는 양자화 통계(CB, SCB)를 포함하지 않아 Hub에서 직접 로드할 수 없습니다. 메타데이터 추가가 우선 과제입니다.
  • CPU 실행 – CPU에는 8‑bit 텐서 코어가 없으며, 향후 소프트웨어 경로를 통해 접근성을 확대할 수 있습니다.
  • 텍스트 외 분야 – 대형 비전, 오디오, 멀티모달 모델에 기술을 확장하는 연구가 진행 중입니다.

Credits: Younes B., Tim Dettmers, and contributors listed in the original blog post.

Sources