Transformers documentation

Conceitos de quantização

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Conceitos de quantização

Um número quantizado só pode assumir um dos valores de um conjunto pequeno, 256 para int8 e 16 para int4, portanto, todo método de quantização introduz erro de arredondamento. Os métodos diferem em como evitam que esse erro prejudique a saída do modelo. Esta página explica as escolhas por trás dessas diferenças: como os números são mapeados para esse conjunto pequeno, quais tensores são quantizados, quais tipos de dados são usados e quando a quantização ocorre.

Se você está começando a usar quantização no Transformers, comece pela Visão geral.

Mapear valores para menos bits

A quantização mapeia um intervalo de valores de alta precisão para o pequeno conjunto de inteiros que um tipo de baixo bit pode representar. O int8 pode representar 256 inteiros, [-128, 127], e o int4 pode representar 16.

Quantização afim é o mapeamento mais comum. Ele pega os valores mínimo e máximo de um tensor e estica o intervalo de inteiros sobre ele. No exemplo abaixo, um tensor cujos valores variam de -1,8 a 2,4 é mapeado para int8, e 0,0 cai em -19.

bf16  -1.8             0.0                    2.4
       ├────────────────┼──────────────────────┤
       ▼                ▼                      ▼
int8  -128             -19                    127

Dois parâmetros definem o mapeamento.

  • O escala S é o tamanho de um passo inteiro nas unidades originais. S=valmax−valminqmax−qmin S = \frac{val_{max} - val_{min}}{q_{max} - q_{min}}

  • O ponto zero Z é o inteiro que representa 0.0. Z=qmin−round(valminS) Z = q_{min} - round\left(\frac{val_{min}}{S}\right)

Um valor x é quantizado para o inteiro q dividindo pela escala e deslocando pelo ponto zero. q=round(xS+Z) q = round\left(\frac{x}{S} + Z\right)

A desquantização inverte o mapeamento e devolve uma aproximação de x. A diferença entre x e sua aproximação é o erro de arredondamento. x≈S⋅(q−Z) x \approx S \cdot (q - Z)

No exemplo acima, S é aproximadamente 0.0165 e Z é -19. O valor 0.5 é quantizado para 11, e 11 é desquantizado para aproximadamente 0.494, um erro de arredondamento de cerca de 0.006.

┌─────┐  quantize   ┌────┐ dequantize   ┌───────┐
│ 0.5 │ ──────────▶ │ 11 │ ───────────▶ │ 0.494 │
└─────┘             └────┘              └───────┘

A quantização afim vem em duas formas.

  • A quantização Assimétrica utiliza as fórmulas acima. Ela mapeia o intervalo exato, de modo que nenhum valor representável é desperdiçado quando os pesos não estão centralizados em zero.
  • A quantização Symmetric pressupõe que o intervalo seja [-a, a] e fixa Z em 0, de modo que apenas a escala é armazenada. O cálculo é mais barato, mas alguns valores representáveis ficam sem uso quando os pesos se inclinam para um lado do zero.

Escolha a granularidade

Uma única escala para um tensor inteiro é barata de armazenar, mas um único valor atípico grande estende o intervalo e afasta os valores representáveis para todos os outros pesos. Grupos menores dão a cada escala um intervalo mais estreito para cobrir.

  • Per-tensor usa uma única escala e um único ponto zero para o tensor inteiro.
  • Per-channel usa uma escala para cada canal de saída, que corresponde a cada linha de uma matriz de pesos.
  • Por grupo, ou por bloco, usa uma escala para cada grupo de valores consecutivos, como 128 pesos. Cada grupo exige armazenamento extra para sua escala.

O diagrama abaixo mostra as três opções em uma matriz de pesos com três linhas. Cada s é uma escala.

per-tensor        per-channel           per-group
┌────────────┐    ┌────────────┐        ┌──────┬──────┐
│            │    │            │ s0     │  s0  │  s1  │
│            │    ├────────────┤        ├──────┼──────┤
│     s      │    │            │ s1     │  s2  │  s3  │
│            │    ├────────────┤        ├──────┼──────┤
│            │    │            │ s2     │  s4  │  s5  │
└────────────┘    └────────────┘        └──────┴──────┘

A maioria dos métodos de baixo número de bits realiza a quantização por grupo. GPTQ usa, por padrão, grupos de 128 pesos, e Fine-grained FP8 usa, por padrão, blocos de 128x128. Os formatos FP4 em Data types incorporam o tamanho do bloco ao próprio formato.

Quantizar pesos ou ativações

Os métodos diferem em quais tensores eles quantizam, e isso determina onde a multiplicação de matrizes é executada.

  • Métodos Weight-only armazenam os pesos em baixa precisão e mantêm as ativações em bf16 ou fp16. Antes de cada matmul, o kernel desquantiza os pesos para um dtype de computação de maior precisão. bitsandbytes 4-bit, AWQ, GPTQ e HQQ funcionam dessa forma. No bitsandbytes, bnb_4bit_compute_dtype define esse dtype de computação.
  • Os métodos de pesos-e-ativações também quantizam as ativações, e o matmul é executado diretamente em baixa precisão. Isso requer hardware que suporte o matmul de baixa precisão, como FP8 em GPUs recentes. O esquema é frequentemente escrito como W8A8 para pesos e ativações de 8 bits.

Métodos apenas de pesos economizam memória, mas nem sempre executam mais rápido, pois a desquantização adiciona trabalho a cada matmul. Eles aceleram a geração quando o modelo é limitado pela memória. Ao gerar um token por vez, uma GPU passa a maior parte de cada etapa lendo pesos da memória, em vez de calcular. Ler pesos de 4 bits move um quarto dos dados que pesos de bf16 movem, e essa economia pode superar o custo da desquantização.

Métodos de pesos e ativações são úteis quando o modelo é limitado por computação, como em prefill ou lotes grandes, pois o próprio matmul de baixa precisão é mais rápido. Alguns métodos alternam entre os dois. O NVFP4 executa um matmul com pesos de 4 bits e ativações de 4 bits para prefill e um matmul com pesos de 4 bits e ativações de 16 bits para decodificação.

Tipos de dados

O tipo de dados define quais valores um número quantizado pode representar.

int8 e int4

int8 e int4 representam inteiros igualmente espaçados e utilizam o mapeamento afim acima. int4 pode representar apenas 16 valores, portanto, perde mais precisão do que int8. Os métodos que utilizam int4 geralmente quantizam por grupo ou usam calibração para manter a precisão.

A maioria dos hardwares não possui um tipo de armazenamento de 4 bits, portanto, dois valores int4 são empacotados em um único byte. Por exemplo, o primeiro valor pode ocupar os 4 bits inferiores e o segundo valor os 4 bits superiores. Os kernels desempacotam os valores antes de realizar os cálculos com eles.

┌──────┐  ┌──────┐        ┌──────┬──────┐
│ val1 │  │ val2 │        │ val2 │ val1 │
│ 0101 │  │ 0011 │   →    │ 0011 │ 0101 │  1 byte
└──────┘  └──────┘        └──────┴──────┘
  int4      int4            high   low

Alguns tipos de 4 bits distribuem seus valores representáveis de forma desigual para corresponder à forma como os pesos são distribuídos. O NF4 do bitsandbytes coloca mais de seus valores perto de zero, onde a maioria dos pesos se encontra.

FP8

O FP8 mantém o layout de ponto flutuante com sinal, expoente e mantissa em 8 bits. Seus valores representáveis estão mais próximos uns dos outros perto de zero e mais espaçados em magnitudes grandes, o que se adequa à distribuição de valores em pesos e ativações.

O FP8 vem em duas variantes.

  • E4M3 possui 4 bits de expoente e 3 bits de mantissa. É mais preciso, mas cobre uma faixa menor. É a escolha usual para pesos e ativações durante a inferência.
  • E5M2 possui 5 bits de expoente e 2 bits de mantissa. Ele cobre uma faixa mais ampla com menos precisão.
E4M3   ┌─┬───────┬─────┐
       │S│E E E E│M M M│
       └─┴───────┴─────┘
E5M2   ┌─┬─────────┬───┐
       │S│E E E E E│M M│
       └─┴─────────┴───┘
  S sign   E exponent   M mantissa

Os matmuls FP8 exigem suporte de hardware, como GPUs NVIDIA com capacidade de computação 8.9 ou superior (Ada, Hopper, Blackwell), AMD Instinct MI300 e Intel XPU. O Transformers oferece suporte a FP8 por meio de Fine-grained FP8, FBGEMM FP8 e compressed-tensors.

FP4 com escalas de bloco

FP4 possui 1 bit de sinal, 2 bits de expoente e 1 bit de mantissa (E2M1). Ele representa apenas 0, 0.5, 1, 1.5, 2, 3, 4 e 6, tanto positivos quanto negativos. Com tão poucos valores, os formatos FP4 não funcionam com uma única escala por tensor. Eles associam uma escala a cada pequeno bloco de valores e armazenam as escalas como parte do formato.

FormatoTamanho do blocoEscala por blocoEscala extra
MXFP432 valores8 bits potência de dois (E8M0)None
NVFP416 valoresFP8Uma escala FP32 por tensor

MXFP4 é um formato de microescala. Uma escala de potência de dois apenas desloca o expoente, o que torna sua aplicação barata, mas ela só pode resultar em potências de dois. O NVFP4 usa blocos menores e uma escala FP8 que pode assumir valores entre potências de dois. Isso preserva mais precisão, ao custo de uma escala por tensor que mantém as escalas de bloco FP8 dentro do intervalo. Four Over Six e FP-Quant também produzem pesos NVFP4 ou MXFP4.

A mesma ideia de blocos aparece em 8 bits. O MXFP8 armazena valores FP8 com escalas de bloco em potências de dois, e o Fine-grained FP8 o carrega.

Codebooks

Abaixo de 4 bits, alguns métodos deixam de arredondar cada peso para o valor representável mais próximo. Eles quantizam grupos de pesos juntos como vetores. Um codebook armazena um conjunto de vetores aprendidos, e cada grupo de pesos é armazenado como o índice do vetor mais próximo. O AQLM usa, por padrão, grupos de 8 pesos e um índice de 16 bits, o que resulta em 2 bits por peso. O VPTQ também utiliza codebooks. Encontrar bons codebooks exige uma otimização longa sobre dados de calibração, portanto, esses modelos são quantizados previamente e carregados como checkpoints.

Quando a quantização ocorre

Um modelo é quantizado após o treinamento ou durante ele. A maioria dos métodos em Transformers quantiza após o treinamento.

Quantização pós-treino

A quantização pós-treinamento (PTQ) quantiza um modelo que já foi treinado, sem alterar o que ele aprendeu. Os métodos de PTQ diferem em relação à necessidade de dados, e isso determina em qual grupo o método se enquadra na tabela de conteúdo da documentação.

  • Sem dados de calibração, as escalas derivam apenas dos pesos, e a quantização é rápida o suficiente para ser executada durante o carregamento do modelo. bitsandbytes, HQQ e torchao funcionam dessa forma. Esses métodos estão listados em Quantizar durante o carregamento.
  • Com dados de calibração, o método executa um pequeno conjunto de dados no modelo e usa as ativações para decidir como quantizar cada camada. A calibração geralmente oferece melhor precisão em 4 bits ou menos, mas leva de minutos a horas.

Os métodos de calibração utilizam as ativações de diferentes maneiras. O GPTQ quantiza uma camada coluna por coluna e ajusta as colunas restantes para compensar o erro de arredondamento de cada coluna. O AWQ identifica os canais de pesos com as maiores ativações e os escala para cima antes do arredondamento, a fim de protegê-los.

O local em que a execução de calibração é realizada determina como você usa o método no Transformers.

  • GPTQ realiza a calibração dentro de from_pretrained quando você passa um dataset para GPTQConfig, por isso está listado em Quantize while loading.
  • Outros métodos de calibração, como AWQ, AutoRound, AQLM e VPTQ, realizam a calibração em suas próprias bibliotecas. O Transformers carrega o resultado, por isso eles estão listados em Carregar checkpoints pré-quantizados.

Treinamento com quantização

Quantization-aware training (QAT) simula a quantização durante o treinamento. A passagem para frente (forward pass) arredonda os valores da mesma forma que a quantização faria, enquanto os gradientes atualizam os pesos em precisão completa, permitindo que o modelo aprenda a tolerar o erro de arredondamento. O QAT geralmente oferece a melhor precisão em larguras de bits muito baixas, mas exige o treinamento do modelo.

Os modelos BitNet são treinados dessa forma, com seus pesos de 1,58 bits no loop, e o Transformers carrega os checkpoints treinados.

Recursos

Update on GitHub