Explicando a Quantização

A Quantização (ou Quantization) é uma das técnicas fundamentais de otimização no campo do Aprendizado de Máquina. Ela permite compactar Grandes Modelos de Linguagem (LLMs), reduzindo drasticamente o consumo de memória (VRAM/RAM) e acelerando o tempo de resposta, com uma perda mínima na capacidade de raciocínio.

Em termos simples, quantizar é reduzir a precisão numérica dos pesos da rede neural.

O que significa Reduzir a Precisão?

Durante o treinamento, as LLMs calculam e armazenam seus bilhões de parâmetros (pesos) usando números de ponto flutuante de alta precisão de 32 bits (FP32) ou 16 bits (FP16 / BF16).

  • Ponto Flutuante de 16 bits (FP16): Guarda números com casas decimais extremamente detalhadas (ex: 0.0048293712). Cada peso ocupa 2 bytes de memória.
  • Inteiro de 4 bits (INT4): Arredonda e mapeia essa gama contínua de valores decimais para um conjunto discreto e limitado de inteiros (ex: inteiros de -8 a 7). Cada peso passa a ocupar apenas 0,5 byte (4 bits).

A Matemática do Mapeamento

O processo de quantização converte um valor contínuo de ponto flutuante x[min,max]x \in [\text{min}, \text{max}] em um valor inteiro discreto q, utilizando dois parâmetros matemáticos: uma Escala (S) e um Ponto Zero (Z).

A fórmula de quantização mais comum (Quantização Uniforme) é:

q=round(xS)+Zq = \text{round}\left( \frac{x}{S} \right) + Z

E para desquantizar o valor durante a execução das operações matemáticas:

x=S(qZ)x’ = S \cdot (q – Z)
  • S (Scale): Define o tamanho dos degraus entre os números.
  • Z (Zero-Point): Ajusta o deslocamento para que o valor zero real da precisão original corresponda exatamente ao zero discreto do inteiro.

Exemplo de cálculo

Para entender a quantização na prática, vamos acompanhar o passo a passo matemático de como um pequeno grupo de pesos de alta precisão (FP32) é comprimido para um formato de baixa precisão de 4 bits (INT4) e depois reconstruído (FP16 aproximado).

Imagine que extraímos uma pequena fatia de uma matriz de pesos de uma camada de um modelo. O modelo original utiliza FP32 (ponto flutuante de 32 bits, ocupando 4 bytes por número).

Nossos 4 pesos originais (X) são:

X=[0.85,0.12,0.45,0.98]X = [-0.85, \quad 0.12, \quad 0.45, \quad 0.98]

Nosso objetivo: Comprimir esses valores para INT4 (inteiro de 4 bits sem sinal), cujos valores possíveis variam apenas no intervalo discreto de 0 a 15 (total de 24=162^4 = 16 níveis).

Passo 1: Calcular a Escala (S) e o Ponto Zero (Z)

Para mapear o intervalo contínuo dos nossos pesos reais [min,max][\text{min}, \text{max}] para o intervalo discreto [qmin,qmax]=[0,15][q_{\text{min}}, q_{\text{max}}] = [0, 15], primeiro identificamos os extremos dos dados originais:

  • min=0.85\text{min} = -0.85
  • max=0.98\text{max} = 0.98
  • qmin=0q_{\text{min}} = 0
  • qmax=15q_{\text{max}} = 15

A. Cálculo da Escala (S)

A escala determina a distância representada por cada degrau do inteiro:

S=maxminqmaxqmin=0.98(0.85)150=1.8315𝟎.𝟏𝟐𝟐S = \frac{\text{max} – \text{min}}{q_{\text{max}} – q_{\text{min}}} = \frac{0.98 – (-0.85)}{15 – 0} = \frac{1.83}{15} \approx \mathbf{0.122}

B. Cálculo do Ponto Zero (Z)

O ponto zero ajusta a escala para que o valor real correspondente a $0.0$ seja representado perfeitamente por um inteiro:

Z=round(minS)=round((0.85)0.122)=round(6.967)=𝟕Z = \text{round}\left( \frac{-\text{min}}{S} \right) = \text{round}\left( \frac{-(-0.85)}{0.122} \right) = \text{round}(6.967) = \mathbf{7}

Passo 2: Quantização (XqX \rightarrow q)

Agora aplicamos a fórmula de quantização uniformizada para cada um dos pesos originais:

q=round(xS)+Zq = \text{round}\left( \frac{x}{S} \right) + Z
  1. Para x1=0.85x_1 = -0.85: q1=round(0.850.122)+7=round(6.967)+7=7+7=𝟎q_1 = \text{round}\left( \frac{-0.85}{0.122} \right) + 7 = \text{round}(-6.967) + 7 = -7 + 7 = \mathbf{0}
  2. Para x2=0.12x_2 = 0.12: q2=round(0.120.122)+7=round(0.983)+7=1+7=𝟖q_2 = \text{round}\left( \frac{0.12}{0.122} \right) + 7 = \text{round}(0.983) + 7 = 1 + 7 = \mathbf{8}
  3. Para x3=0.45x_3 = 0.45: q3=round(0.450.122)+7=round(3.688)+7=4+7=𝟏𝟏q_3 = \text{round}\left( \frac{0.45}{0.122} \right) + 7 = \text{round}(3.688) + 7 = 4 + 7 = \mathbf{11}
  4. Para x4=0.98x_4 = 0.98: q4=round(0.980.122)+7=round(8.032)+7=8+7=𝟏𝟓q_4 = \text{round}\left( \frac{0.98}{0.122} \right) + 7 = \text{round}(8.032) + 7 = 8 + 7 = \mathbf{15}

Resultado Comprimido

Em vez de salvar os 4 números flutuantes pesados de 32 bits, o modelo salva apenas a sequência de inteiros de 4 bits:

q=[0,8,11,15]q = [0, \quad 8, \quad 11, \quad 15]

Além do vetor q, o modelo precisa armazenar os metadados de bloco: S = 0.122 e Z = 7.

Ganho de memória: Cada peso q ocupa agora apenas 4 bits (0,5 byte) em vez de 32 bits (4 bytes). Redução de 8x no tamanho dos pesos.

Passo 3: Desquantização na Inferência (qXq \rightarrow X’)

Quando a LLM precisa realizar as multiplicações de matrizes durante a geração de um token, ela desquantiza temporariamente esses inteiros de volta para ponto flutuante usando a fórmula inversa:

X=S(qZ)X’ = S \cdot (q – Z)
  1. X1=0.122(07)=0.122(7)=𝟎.𝟖𝟓𝟒X’_1 = 0.122 \cdot (0 – 7) = 0.122 \cdot (-7) = \mathbf{-0.854} (Original: -0.85)
  2. X2=0.122(87)=0.122(1)=𝟎.𝟏𝟐𝟐X’_2 = 0.122 \cdot (8 – 7) = 0.122 \cdot (1) = \mathbf{0.122} (Original: 0.12)
  3. X3=0.122(117)=0.122(4)=𝟎.𝟒𝟖𝟖X’_3 = 0.122 \cdot (11 – 7) = 0.122 \cdot (4) = \mathbf{0.488} (Original: 0.45)
  4. X4=0.122(157)=0.122(8)=𝟎.𝟗𝟕𝟔X’_4 = 0.122 \cdot (15 – 7) = 0.122 \cdot (8) = \mathbf{0.976} (Original: 0.98)

Avaliação do Erro de Quantização

Analisando a diferença entre os pesos originais e os desquantizados:

Valor Original (X)Valor Reconstruído (X′)Erro Absoluto (∣X−X′∣)
-0.85-0.8540.004
0.120.1220.002
0.450.4880.038
0.980.9760.004

A perda de precisão é na casa dos centésimos. Para bilhões de conexões em uma rede neural, esse pequeno desvio numérico é absorvido pela robustez do modelo, enquanto os ganhos em consumo de VRAM e velocidade de execução são significativos.

Deixe um comentário