GPTQ 양자화
GPTQ 양자화
GPTQ는 GPT 계열 LLM을 위한 양자화 방법으로, 근사 2차 정보(approximate second-order information)에 기반한 원샷(one-shot) 가중치 양자화를 사용해요. 이 문서에서는 양자화 모델을 Hugging Face transformers로 사용하는 방법과 AutoGPTQ로 나만의 모델을 양자화하는 방법을 보여드려요.
출처: 문서
본문
⚠️ 주의: 이 페이지는 Qwen3 기준으로 업데이트 예정이에요.
GPTQ는 GPT 계열 LLM을 위한 양자화 방법으로, 근사 2차 정보에 기반한 원샷 가중치 양자화를 사용해요. 이 문서에서는 양자화 모델을 Hugging Face transformers로 사용하는 방법과 AutoGPTQ로 나만의 모델을 양자화하는 방법을 보여드려요.
Hugging Face transformers로 GPTQ 모델 사용하기
📝 참고: 공식 Qwen2.5 GPTQ 모델을
transformers로 사용하려면optimum>=1.20.0과 호환되는 버전의transformers,auto_gptq가 설치되어 있어야 해요.
다음 명령으로 설치할 수 있어요:
pip install -U "optimum>=1.20.0"
이제 transformers가 AutoGPTQ를 공식 지원하므로, 양자화 모델을 transformers로 직접 사용할 수 있어요. Qwen2.5의 각 크기마다 Int4와 Int8 GPTQ 양자화 모델을 모두 제공해요. 다음은 Qwen2.5-7B-Instruct-GPTQ-Int4를 실행하는 아주 간단한 코드예요:
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4"
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
prompt = "Give me a short introduction to large language models."
messages = [
{"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
{"role": "user", "content": prompt},
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
generated_ids = model.generate(
**model_inputs,
max_new_tokens=512,
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
vLLM으로 GPTQ 모델 사용하기
vLLM이 GPTQ를 지원하므로, 우리가 제공하는 GPTQ 모델이나 AutoGPTQ로 훈련한 모델을 vLLM과 직접 사용할 수 있어요. 가능하면 더 효율적인 GPTQ Marlin 커널을 자동으로 사용해요.
실제로 사용법은 vLLM의 기본 사용법과 동일해요. vLLM과 Qwen2.5-7B-Instruct-GPTQ-Int4로 OpenAI-API 호환 API를 실행하는 간단한 예시를 제공할게요:
셸에서 다음을 실행해 OpenAI 호환 API 서비스를 시작하세요:
vllm serve Qwen2.5-7B-Instruct-GPTQ-Int4
그 다음 아래처럼 API를 호출할 수 있어요:
curl http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "Qwen2.5-7B-Instruct-GPTQ-Int4",
"messages": [
{"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
{"role": "user", "content": "Tell me something about large language models."}
],
"temperature": 0.7,
"top_p": 0.8,
"repetition_penalty": 1.05,
"max_tokens": 512
}'
또는 아래처럼 openai Python 패키지로 API 클라이언트를 사용할 수 있어요:
from openai import OpenAI
openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8000/v1"
client = OpenAI(
api_key=openai_api_key,
base_url=openai_api_base,
)
chat_response = client.chat.completions.create(
model="Qwen2.5-7B-Instruct-GPTQ-Int4",
messages=[
{"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
{"role": "user", "content": "Tell me something about large language models."},
],
temperature=0.7,
top_p=0.8,
max_tokens=512,
extra_body={
"repetition_penalty": 1.05,
},
)
print("Chat response:", chat_response)
AutoGPTQ로 나만의 모델 양자화하기
나만의 모델을 GPTQ 양자화 모델로 만들고 싶다면 AutoGPTQ를 사용하는 것을 권장해요. 패키지의 최신 버전을 소스 코드에서 설치하는 것을 권장해요:
git clone https://github.com/AutoGPTQ/AutoGPTQ
cd AutoGPTQ
pip install -e .
Qwen2.5-7B를 기반으로 파인튜닝한 Qwen2.5-7B-finetuned라는 모델이 있고, 이를 Alpaca 같은 여러분만의 데이터셋으로 만들었다고 가정해 볼게요. 나만의 GPTQ 양자화 모델을 만들려면 캘리브레이션에 훈련 데이터를 사용해야 해요. 아래에 실행할 간단한 데모를 제공할게요:
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer
# Specify paths and hyperparameters for quantization
model_path = "your_model_path"
quant_path = "your_quantized_model_path"
quantize_config = BaseQuantizeConfig(
bits=8, # 4 or 8
group_size=128,
damp_percent=0.01,
desc_act=False, # set to False can significantly speed up inference but the perplexity may slightly bad
static_groups=False,
sym=True,
true_sequential=True,
model_name_or_path=None,
model_file_base_name="model"
)
max_len = 8192
# Load your tokenizer and model with AutoGPTQ
# To learn about loading model to multiple GPUs,
# visit https://github.com/AutoGPTQ/AutoGPTQ/blob/main/docs/tutorial/02-Advanced-Model-Loading-and-Best-Practice.md
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoGPTQForCausalLM.from_pretrained(model_path, quantize_config)
하지만 모델을 여러 GPU에 로드하고 싶다면 device_map 대신 max_memory를 사용해야 해요. 예시는 다음과 같아요:
model = AutoGPTQForCausalLM.from_pretrained(
model_path,
quantize_config,
max_memory={i: "20GB" for i in range(4)}
)
그 다음 캘리브레이션 데이터를 준비해야 해요. 샘플을 리스트에 넣기만 하면 되고, 각 샘플은 텍스트예요. 파인튜닝 데이터를 캘리브레이션에 직접 사용하므로 먼저 ChatML 템플릿으로 포맷해요. 예를 들어:
import torch
data = []
for msg in dataset:
text = tokenizer.apply_chat_template(msg, tokenize=False, add_generation_prompt=False)
model_inputs = tokenizer([text])
input_ids = torch.tensor(model_inputs.input_ids[:max_len], dtype=torch.int)
data.append(dict(input_ids=input_ids, attention_mask=input_ids.ne(tokenizer.pad_token_id)))
여기서 각 msg는 아래와 같은 전형적인 채팅 메시지예요:
[
{"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
{"role": "user", "content": "Tell me who you are."},
{"role": "assistant", "content": "I am a large language model named Qwen..."}
]
그 다음 한 줄의 코드로 캘리브레이션 과정을 실행하세요:
import logging
logging.basicConfig(
format="%(asctime)s %(levelname)s [%(name)s] %(message)s", level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S"
)
model.quantize(data, cache_examples_on_gpu=False)
마지막으로 양자화 모델을 저장하세요:
model.save_quantized(quant_path, use_safetensors=True)
tokenizer.save_pretrained(quant_path)
아쉽게도 save_quantized 메서드는 sharding을 지원하지 않아요. sharding이 필요하다면 모델을 로드한 뒤 transformers의 save_pretrained를 사용해 모델을 저장·샤딩해야 해요. 이 점만 제외하면 모든 것이 아주 간단해요. 즐기세요!
알려진 문제
Qwen2.5-72B-Instruct-GPTQ-Int4가 제대로 생성을 멈추지 못함
- 모델: Qwen2.5-72B-Instruct-GPTQ-Int4
- 프레임워크: vLLM, AutoGPTQ (Hugging Face transformers 포함)
- 설명: 생성이 제대로 멈추지 못해요. 멈춰야 할 지점 이후에도 계속 생성되며, 단일 문자, 구문 또는 단락 등 반복된 텍스트가 생성돼요.
- 해결 방법: 다음을 고려해 볼 수 있어요.
- 16비트 부동소수점 원본 모델 사용
- 비정상 생성 확률을 줄이기 위해 AWQ 변형 또는 llama.cpp 기반 모델 사용
Qwen2.5-32B-Instruct-GPTQ-Int4가 vLLM으로 멀티 GPU에서 깨짐
- 모델: Qwen2.5-32B-Instruct-GPTQ-Int4
- 프레임워크: vLLM
- 설명: 멀티 GPU 배포 시
!!!!!!!!!!!!!!!!!!같은 깨진 텍스트만 생성돼요. - 해결 방법: 다음 각각을 고려할 수 있어요.
- AWQ 또는 GPTQ-Int8 변형 사용
- 단일 GPU 사용
- 지연 시간과 처리량이 크게 중요하지 않다면 Hugging Face
transformers사용
문제 해결
transformers와 auto_gptq를 쓰는데 로그가 CUDA extension not installed.라고 하고 추론이 느려요. — auto_gptq가 여러분 환경과 호환되는 fused CUDA 커널을 찾지 못해 일반 구현으로 대체한 거예요. 설치 가이드를 따라 미리 빌드된 wheel을 설치하거나 소스에서 auto_gptq를 설치해 보세요.
vLLM으로 자체 양자화한 Qwen2.5-72B-Instruct-GPTQ에서 ValueError: ... must be divisible by ...가 발생해요. — 자체 양자화 모델의 intermediate size가 공식 Qwen2.5-72B-Instruct-GPTQ 모델과 달라서예요.
양자화 후 양자화 가중치의 크기는 그룹 크기(보통 128)로 나눠져요. Qwen2.5-72B의 FFN 블록 intermediate size는 29568이에요. 안타깝게도 29568 ÷ 128 = 231이에요. 어텐션 헤드 수와 가중치 차원이 텐서 병렬 크기로 나누어져야 하므로, tensor_parallel_size=1(즉 GPU 카드 1개)에서만 양자화 모델을 실행할 수 있음을 뜻해요.
해결 방법은 intermediate size를 128 × 8 = 1024로 나누어 떨어지게 만드는 거예요. 이를 위해 가중치를 0으로 패딩해야 해요. 가중치를 0으로 패딩하기 전과 후는 수학적으로 동등하지만, 실제 결과는 약간 다를 수 있어요.
다음을 시도해 보세요:
import torch
from torch.nn import functional as F
from transformers import AutoModelForCausalLM
# must use AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-72B-Instruct", torch_dtype="auto")
# this size is Qwen2.5-72B only
pad_size = 128
sd = model.state_dict()
for i, k in enumerate(sd):
v = sd[k]
print(k, i)
# interleaving the padded zeros
if ('mlp.up_proj.weight' in k) or ('mlp.gate_proj.weight' in k):
prev_v = F.pad(v.unsqueeze(1), (0, 0, 0, 1, 0, 0)).reshape(29568*2, -1)[:pad_size*2]
new_v = torch.cat([prev_v, v[pad_size:]], dim=0)
sd[k] = new_v
elif 'mlp.down_proj.weight' in k:
prev_v= F.pad(v.unsqueeze(2), (0, 1)).reshape(8192, 29568*2)[:, :pad_size*2]
new_v = torch.cat([prev_v, v[:, pad_size:]], dim=1)
sd[k] = new_v
# this is a very large file; make sure your RAM is enough to load the model
torch.save(sd, '/path/to/padded_model/pytorch_model.bin')
이렇게 하면 패딩된 체크포인트가 지정된 디렉터리에 저장돼요. 그 다음 원본 체크포인트의 다른 파일들을 새 디렉터리에 복사하고 config.json의 intermediate_size를 29696으로 수정하세요. 마지막으로 저장된 모델 체크포인트를 양자화할 수 있어요.