2️⃣LLM Quantization

LLM Quantization 트렌드

SOTA quantization methods 양자화 방법론

  • LMNI.T8(only8-bit) - Aug 2022 (Dettmers et al.)

  • QLORA (only 4-bit) - May 2023 (Dettmers et al.)

  • AWQ- Jun 2023 (Lin et al.)

  • GPTQ - Oct 2022 (Frantar et al.)

  • SmoothQuant - Nov 2022 (Xiao et al.)

최근 SOTA 방법론은 대다수가 2-bit 양자화 사용

  • QuIP# - Jul 2023 (Tseng et al.()

  • HQQ - November 2023 (Badri et al.)

  • AQLM - Feb 2024 (Egiazarian et al.)

LLM의 양자화 특징

다음과 같은 특징으로 설계되었습니다:

  • LM을 더 작게 만들고(따라서 더 빠르게)

  • 성능 저하 최소화

  • 오픈소스 LLM에 모두 적용

양자화에서 Calibration 시에 요구 사항:

  • 데이터 집합에서 추론 실행

  • 양자화 매개 변수를 최적화하여 양자화 오류 최소화하기

일부 정량화 방법은 조정 없이 모든 모델에 바로 적용할 수 있습니다.

  • 예: NLP, 컴퓨터 비전, 오디오, 멀티모달

LLM 양자화 주요 방법론:

  1. Linear Quantization (선형 양자화)

  2. LLM.INT8 (8-bit만 해당t)

  3. QLoRA(4-bit만 해당)

  4. HQQ(최대 2-bit)

예를 들면

Llama2-70B parameter model을 비교하면

  • 280 GB storage in FP32 (32-bit precision)

  • Reduce to 40GB fi store in 4-bit precision

  • 280G에서 40GB로 7 배 사이즈를 줄여준다

Llama2-7B 모델의 경우

  • 28 GB storage in FP32 (32-bit precision)

  • 모델 사이즈를 4GB로 줄임 (설정은 4-bit precision의 GGUF format)

Fine-tune Quantization 종류

Fine-tuning Quantization Model

  • 양자화에서 정확도가 그대로 재현

  • 특정 사용 사례 및 애플리케이션에 맞게 모델의 Fine-tuning 동시에 가능

QAT: Fine-tune with Quantization Aware Training

  • 정량화된 버전이 최적의 성능을 발휘할 수 있도록 모델을 미세 조정 합니다.

  • Post Training Quantization(PTQ) 기법과는 호환되지 않습니다.

  • Linear Quantization(선형 양자화) 방법은 PTQ의 예입니다.

PEFT(Parameters efficient fine-tuning)

  • 전체 미세 조정과 동일한 성능을 유지하면서 모델의 학습 가능한 매개변수 수를 대폭 줄일 수 있습니다.

  • 대표적으로 PEFT +QLoRA 활용: https://pytorch.org/blog/finetune-llms/

QLoRA 방법론

  1. QLoRA는 사전 학습 된 기본 가중치(그림의 blue 컬러)를 4비트 정밀도로 정량화합니다.

  2. Low Rank Adaptor(LoRA) 가중치의 정밀도(그림의 orang 컬러)와 일치합니다.

  3. 모델은 사전 학습된 가중치(blue)와 어댑터 가중치(orange)의 활성화를 추가할 수 있습니다.

  4. 이 두 활성화의 합은 네트워크의 다음 계층에 입력으로 제공될 수 있습니다.

-> QLoRA를 사용하면 더 효율적인 미세 조정 + 더 작은 모델을 얻을 수 있습니다!

Quantization Theory 실습

Linear Quantization(선형 양자화)에 대하여 알아보겠습니다.

#%pip install sentencepiece
#%pip install quanto==0.0.11
import torch

def named_module_tensors(module, recurse=False):
    for named_parameter in module.named_parameters(recurse=recurse):
      name, val = named_parameter
      flag = True
      if hasattr(val,"_data") or hasattr(val,"_scale"):
        if hasattr(val,"_data"):
          yield name + "._data", val._data
        if hasattr(val,"_scale"):
          yield name + "._scale", val._scale
      else:
        yield named_parameter

    for named_buffer in module.named_buffers(recurse=recurse):
      yield named_buffer

def dtype_byte_size(dtype):
    """
    Returns the size (in bytes) occupied by one parameter of type `dtype`.
    """
    import re
    if dtype == torch.bool:
        return 1 / 8
    bit_search = re.search(r"[^\d](\d+)$", str(dtype))
    if bit_search is None:
        raise ValueError(f"`dtype` is not a valid dtype: {dtype}.")
    bit_size = int(bit_search.groups()[0])
    return bit_size // 8

def compute_module_sizes(model):
    """
    Compute the size of each submodule of a given model.
    """
    from collections import defaultdict
    module_sizes = defaultdict(int)
    for name, tensor in named_module_tensors(model, recurse=True):
      size = tensor.numel() * dtype_byte_size(tensor.dtype)
      name_parts = name.split(".")
      for idx in range(len(name_parts) + 1):
        module_sizes[".".join(name_parts[:idx])] += size

    return module_sizes

Without Quantization

model_name = "google/flan-t5-small"
import sentencepiece as spm
from transformers import T5Tokenizer, T5ForConditionalGeneration

tokenizer = T5Tokenizer.from_pretrained("google/flan-t5-small")
model = T5ForConditionalGeneration.from_pretrained("google/flan-t5-small")
input_text = "Hello, my name is "
input_ids = tokenizer(input_text, return_tensors="pt").input_ids

outputs = model.generate(input_ids)
print(tokenizer.decode(outputs[0]))
module_sizes = compute_module_sizes(model)

print(f"The model size is {module_sizes[''] * 1e-9} GB")
The model size is 0.307844608 GB

Quantize the model (8-bit precision)

from quanto import quantize, freeze
import torch

quantize(model, weights=torch.int8, activations=None)
print(model)

Freeze the model

freeze(model)
module_sizes = compute_module_sizes(model)
print(f"The model size is {module_sizes[''] * 1e-9} GB")
The model size is 0.12682868 GB

Try running inference on the quantized model

input_text = "Hello, my name is "
input_ids = tokenizer(input_text, return_tensors="pt").input_ids

outputs = model.generate(input_ids)
print(tokenizer.decode(outputs[0]))

Linear Quantization과 Downcasting 방법의 차이점을 요약하면 다음과 같습니다:

  • 모델을 다운캐스팅할 때는 모델의 파라미터를 보다 간결한 데이터 유형(bfloat16)으로 변환합니다. 추론하는 동안 모델은 이 데이터 유형으로 계산을 수행하고 활성화는 이 데이터 유형으로 이루어집니다. 다운캐스팅은 bfloat16 데이터 유형으로 작동할 수 있지만 더 작은 데이터 유형에서는 모델 성능이 저하될 수 있으며, 정수 데이터 유형(이 단원의 int8과 같은)으로 변환하는 경우에는 작동하지 않을 수 있습니다.

  • linear quantization를 사용하여 추론 중에 압축된 데이터 유형에서 원래의 FP32 데이터 유형으로 다시 변환함으로써 양자화된 모델이 원래 모델에 훨씬 가까운 성능을 유지할 수 있도록 했습니다. 따라서 모델이 예측을 할 때 행렬 곱셈은 FP32로, 활성화는 FP32로 수행합니다. 이를 통해 이 예제에서는 int8과 같이 bfloat16보다 작은 데이터 유형으로 모델을 정량화할 수 있습니다.

Last updated