[LLM] Quantization의 이론과 적용

jodog0412·2024년 9월 22일

quantization

목록 보기
1/1

지난달에 GPTQ, llama.cpp를 바탕으로 LLM Quantization을 간단히 진행하였습니다.
따라서, 이번 글에는 딥러닝에서의 Quantization에 대한 개념을 정리할 예정입니다. LLM에 대한 Quantization이 실제로 어떻게 이루어지는지는 다음 게시글에 기록할 예정입니다.

2024-10-23
이전에 썼던 글이 마음에 안들어서 싹 다 갈아엎었습니다! huggingface의 글을 바탕으로 내용을 다시 작성하였습니다.

1. What is Quantization?

Quantization은 딥러닝 모델 추론(inference)에서의 계산량 및 메모리 소모량을 줄이기 위해 weights와 activation를 낮은 정밀도의 데이터 유형으로 재표현하는 것을 의미합니다. 32-bit floating point에서 8-bit 정수로 데이터 타입을 전환하는 것이 대표적인 예시입니다.

2. How to quantize?

float32에서 int8로 양자화하는 것은 tricky합니다. float32로 매우 넓은 범위의 값을 표현할 수 있는 반면, int8은 오직 256개의 값만 표현할 수 있습니다.

다음의 아이디어는 float32 값들의 범위 [a, b]를 int8공간으로 투영하는 최선의 길을 찾습니다.

Calculation

  1. [a, b] 범위의 부동소수점 x를 고려해봅시다.
    x=S(xqZ)x=S*(x_q-Z)
    xqx_q : 부동소수점 xint8 value
    SS : scale, 양의 부동소수점
    ZZ : zero-point, int8 value corresponding to the value 0 in the float32 realm.
    float32에서의 0을 나타내는 int8의 값이 0이 아닐 수 있음을 유의합시다!
  1. 양자화된 값 xqx_q는 다음과 같이 계산됩니다.
    xq=round(x/S+Z)x_q=round(x/S + Z)
  1. [a, b] 범위 밖에 있는 float32 값은 가장 가까운 경계값으로 clip됩니다. 즉, 부동소수점 x에 대해 다음과 같은 관계식이 존재합니다.
    xq=clip(round(x/S+Z), round(a/S+Z), round(b/S+Z))x_q=clip(round(x/S+Z),\ round(a/S+Z),\ round(b/S+Z))
    round(x/S+Z): float32 value를 갖는 x를 정수로 변환
    round(a/S+Z): 범위의 하한인 a를 정수로 변환
    round(b/S+Z): 범위의 상한인 b를 정수로 변환
    clip(, round(a/S+Z), round(b/S+Z)): 이때 나온 정수값이 범위 [a,b] 내에 있는지 확인합니다. 범위를 벗어난 값은 가장 가까운 값으로 clip됩니다.

per-tensor and per-channel quantization

정확도(accuracy)와 지연(latency)의 trade-off 관계에 따라, quantization parameter의 세분화를 조정할 수 있습니다:

Per-tensor Quantization

  • Quantization parameters가 tensor 단위로 계산할 수 있습니다. 즉, tensor당 한 쌍의 (S, Z)가 사용됩니다.

Per-channel Quantization

  • Quantization parameters 또한 channel 단위로 계산될 수 있습니다. 즉, 텐서의 차원 중 하나를 따라 요소당 (S, Z) 한 쌍을 저장할 수 있습니다.
  • 이는 정확도를 더 향상시키는 대신, 더 많은 메모리를 소모합니다.

Calibration

approach

위의 섹션에서는 float32 에서 int8 로 어떻게 양자화되는지를 설명하였습니다.

그러나 한 가지 질문이 남아있습니다: float32 값들의 [a, b] 범위는 어떻게 결정되는 걸까요?

Calibration은 float32의 범위를 계산하는 단계입니다.

가중치의 경우 양자화 시점에 실제 범위를 알 수 있으므로 매우 쉽습니다.

그러나 활성화 함수의 경우에는 조금 불명확하며, 이에 따라 다양한 접근법이 존재합니다.

  1. Post training dynamic quantization (PTQ-dynamic)

    각 활성화 함수에 대한 범위가 추론 과정(inference-process) 에서 계산됩니다. 많은 작업 없이도 좋은 결과를 얻을 수 있지만, 매번 범위를 계산할 때 발생하는 오버헤드로 정적 정량화보다 약간 느릴 수 있습니다. 또한 특정 하드웨어에서는 사용할 수 없는 옵션이기도 합니다.

  2. Post training static quantization(PTQ-static)

    각 활성화 함수에 대한 범위는 일반적으로 대표 데이터를 모델에 전달하고 활성화 함수 값을 기록하여 양자화 과정(quantization-process) 에서 계산됩니다.

    실제 단계는 다음과 같습니다:

    1. observers를 활성화 함수에 배치하여 활성화 함수의 값들을 기록합니다.
    2. calibration dataset에 대해 일정한 횟수의 forward passes를 수행합니다.
      (보통 200 examples).
    3. 각 계산의 범위는 calibration technique에 따라 계산됩니다.
  3. Quantization aware training(QAT)

    각 활성화 함수에 대한 범위는 훈련 과정(training-process) 에서 계산되며, 위의 PTQ-static과 calibration 아이디어는 같습니다. 그러나 ‘가짜 양자화’ 연산자가 observers 대신 사용됩니다. observer처럼 값을 기록하지만, 양자화로 인해 유발되는 오류를 simulate하여 모델이 이에 적응할 수 있도록 합니다.

technique

PTQ-static 및 QAT 모두에 대해 가장 일반적인 calibration technique를 정의할 필요가 있습니다

  • Min-max: 계산된 범위는 [최소 관측값(min observed value), 최대 관측값(max observed value)]이며, 가중치에 잘 적용됩니다.
  • Moving average min-max: 계산된 범위는 [이동 평균 최소 관측값, 이동 평균 최대 관측값]이며, 활성화 함수에 잘 적용됩니다.
  • Histogram: 최소값 및 최대값과 함께 value의 히스토그램을 기록하고, 아래의 기준에 따라 선택합니다:
    • Entropy: 범위는 전체 정밀도(full-precision)와 양자화된 데이터(quantized-data)사이의 error를 최소화하는 값으로 계산됩니다.
    • Mean Square Error(MSE): 범위는 정밀도와 양자화된 데이터 사이의 mean-square-error를 최소화하는 값으로 계산됩니다.
    • Percentile: 범위는 관찰된 값들에 대해 주어진 백분위수 값 p를 사용하여 계산됩니다. 계산 범위에 관찰값의 p%가 포함되게끔 하는 아이디어입니다. 이는 affine quantization을 수행할 때 가능하지만, symmetric quantization를 수행할 때 항상 정확히 일치하는 것은 아닙니다.
profile
오픈소스에 관심이 많은 개발자입니다 :D

0개의 댓글