GPTQ로 LLM 양자화하기

GPTQ로 LLM 양자화하기

LLM은 크기가 커서 GPU 메모리에 통째로 올리기 어려운 경우가 많아요. 이때 GPTQ 양자화를 쓰면 언어 모델을 8비트, 4비트, 심지어 3비트나 2비트까지 줄일 수 있어요. 성능이 크게 떨어지지 않으면서 추론 속도는 빨라지고, 대부분의 GPU 하드웨어에서 지원된답니다. Optimum이 GPT-QModel 라이브러리와 손잡고 GPTQ 양자화를 아주 간단한 API로 제공해요.

출처: Hugging Face Optimum — Quantization (GPTQ)

준비물 (Requirements)

아래 코드를 실행하려면 다음 패키지가 필요해요.

  • Optimum 라이브러리: pip install --upgrade optimum

  • GPT-QModel: pip install "gptqmodel>=7.0.0"

  • 최신 transformers 라이브러리를 소스에서 설치: pip install --upgrade git+https://github.com/huggingface/transformers.git

  • 최신 accelerate 라이브러리: pip install --upgrade accelerate

모델 로드하고 양자화하기

모델을 양자화할 때는 [optimum.gptq.GPTQQuantizer] 클래스를 써요. 몇 가지 인자를 넘겨줘야 하는데, 이렇게 돼요.

  • 비트 수: bits
  • 양자화를 캘리브레이션할 데이터셋: dataset
  • 데이터셋 처리에 쓰는 모델 시퀀스 길이: model_seqlen
  • 양자화할 블록 이름: block_name_to_quantize

Transformers 통합을 쓰면 block_name_to_quantizemodel_seqlen은 라이브러리가 알아서 찾아주니 안 넘겨도 돼요. 다만 커스텀 모델이라면 직접 지정해야 하고, 양자화 전에 모델을 torch.float16으로 바꿔두어야 해요.

from transformers import AutoModelForCausalLM, AutoTokenizer
from optimum.gptq import GPTQQuantizer, load_quantized_model
import torch
model_name = "facebook/opt-125m"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)

quantizer = GPTQQuantizer(bits=4, dataset="c4", block_name_to_quantize = "model.decoder.layers", model_seqlen = 2048)
quantized_model = quantizer.quantize_model(model, tokenizer)

⚠️ GPTQ 양자화는 지금은 텍스트 모델에서만 동작해요. 그리고 양자화 과정은 하드웨어에 따라 오래 걸릴 수 있어요(175B 모델은 NVIDIA A100 기준 약 4 GPU 시간). 양자화하려는 모델이 이미 Hub에 GPTQ 버전으로 올라와 있지 않은지 먼저 확인해 보세요.

모델 저장하기

모델을 저장하려면 [optimum.gptq.GPTQQuantizer] 클래스의 save 메서드를 써요. 모델의 state dict와 양자화 설정을 담은 폴더를 만들어주죠.

save_folder = "/path/to/save_folder/"
quantizer.save(model,save_folder)

양자화된 가중치 불러오기

양자화된 가중치는 [optimum.gptq.load_quantized_model] 함수로 불러와요. Accelerate 라이브러리를 쓰면 메모리를 덜 쓰면서 더 빨리 모델을 불러올 수 있는데, 모델을 빈 가중치로 초기화한 뒤 가중치를 다음 단계에서 로드해야 해요.

from accelerate import init_empty_weights
with init_empty_weights():
    empty_model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)
empty_model.tie_weights()
quantized_model = load_quantized_model(empty_model, save_folder=save_folder, device_map="auto")

더 빠른 추론을 위한 커널 선택

GPT-QModel로 GPTQ 모델을 불러오면 런타임이 현재 하드웨어와 양자화된 모델에 맞는 추론 커널을 자동으로 선택해요. 그래서 일반적인 경우엔 backend를 명시하지 않는 게 좋아요.

from optimum.gptq import GPTQQuantizer, load_quantized_model
import torch

from accelerate import init_empty_weights
with init_empty_weights():
    empty_model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)
empty_model.tie_weights()
quantized_model = load_quantized_model(
    empty_model,
    save_folder=save_folder,
    device_map="auto",
)

PEFT로 파인튜닝을 한다면 특별한 이유가 없으면 기본 자동 백엔드 선택을 그대로 쓰는 게 좋아요. 커널별 벤치마크는 Optimum 저장소에서 확인할 수 있어요.

양자화된 모델 파인튜닝

Hugging Face 생태계에서 어댑터가 공식 지원되면서, GPTQ로 양자화한 모델도 파인튜닝할 수 있어요. 자세한 내용은 peft 라이브러리를 참고하세요.

더 알아보기 (Learn more)