Guia visual sobre quantização de LLM
(newsletter.maartengrootendorst.com)- Modelos de linguagem de grande porte (LLMs) são grandes demais para rodar em hardware comum e, como normalmente têm bilhões de parâmetros, exigem GPUs com grande quantidade de VRAM
- Por isso, cada vez mais pesquisas têm se concentrado em tornar esses modelos menores por meio de treinamento aprimorado, adaptadores etc., e uma das principais técnicas dessa área é a quantização (Quantization)
Part 1: o "problema" dos modelos de linguagem de grande porte
- LLM (Large Language Model) recebeu esse nome de acordo com a quantidade de parâmetros que contém
- Esses modelos geralmente incluem bilhões de parâmetros (em sua maioria pesos), o que pode gerar um custo de armazenamento bastante alto
- Durante a inferência, as ativações são geradas pelo produto entre entradas e pesos, e também podem ser consideravelmente grandes
- Portanto, tenta-se representar bilhões de valores da forma mais eficiente possível, minimizando ao mesmo tempo o espaço necessário para armazenar um determinado valor
Como representar valores numéricos
- Um dado valor muitas vezes é representado como um número de ponto flutuante (real)
- Esses valores são representados em "bits", e o padrão IEEE-754 descreve como os bits indicam uma das funções de sinal, expoente e mantissa (fraction) para representar um valor
- Em geral, quanto mais bits são usados para representar um valor, maior é a precisão
- Quanto mais bits disponíveis, maior também é a faixa de valores que pode ser representada
Restrições de memória
- Supondo um modelo com 70 bilhões de parâmetros, se for usado FP32 (full-precision), seriam necessários 280 GB de memória apenas para carregar o modelo
- Por isso, é muito importante minimizar o número de bits usados para representar os parâmetros do modelo, mas quando a precisão diminui, a acurácia do modelo também costuma cair
- O objetivo é reduzir o número de bits usados para representar valores mantendo a acurácia, e é exatamente aí que entra a quantização (quantization)
Part 2: introdução à quantização
- A quantização busca reduzir a precisão dos parâmetros do modelo de uma largura de bits alta (por exemplo, ponto flutuante de 32 bits) para uma largura de bits menor (por exemplo, inteiro de 8 bits)
- Cada vez que o número de bits é reduzido, é feito um mapeamento para "comprimir" os parâmetros originais em uma representação de poucos bits
Tipos de dados comuns
FP16
- Ao passar de FP32 para FP16 (half precision), a faixa de valores que o FP16 pode assumir se torna significativamente menor que a do FP32
BF16
- Para obter uma faixa de valores semelhante à do FP32, foi introduzido o bfloat16, uma espécie de "FP32 truncado"
- BF16 usa a mesma quantidade de bits que FP16, mas pode ter uma faixa de valores mais ampla e é frequentemente usado em aplicações de deep learning
INT8
- Ao reduzir ainda mais o número de bits, a representação deixa de se parecer com ponto flutuante e se aproxima de uma representação baseada em inteiros
Quantização simétrica
- A faixa de valores originais em ponto flutuante é mapeada para uma faixa simétrica em torno de 0 no espaço quantizado
- O valor quantizado de 0 no espaço de ponto flutuante é exatamente 0 no espaço quantizado
Quantização assimétrica
- Diferentemente da quantização simétrica, ela não é simétrica em torno de 0
- Mapeia o valor mínimo (β) e o valor máximo (α) da faixa em ponto flutuante para o mínimo e o máximo da faixa quantizada
- Um dos métodos é chamado de quantização com zero-point
Mapeamento de faixa e clipping
- Se toda a faixa de um vetor for mapeada, outliers podem fazer com que todos os valores pequenos sejam mapeados para a mesma representação de poucos bits, perdendo capacidade de diferenciação
- Em vez disso, pode-se optar por fazer clipping de determinados valores
- Clipping consiste em definir uma faixa dinâmica diferente para os valores originais, fazendo com que todos os outliers tenham o mesmo valor
- O erro de quantização dos valores que não são outliers diminui bastante, mas o erro de quantização dos outliers aumenta
Calibração (Calibration)
Pesos (e bias)
- Pesos e bias podem ser considerados valores estáticos conhecidos antes da execução do modelo
- Como há muito menos bias do que pesos, eles são mantidos em precisão mais alta (por exemplo, INT16), e o principal esforço de quantização se concentra nos pesos
- Técnicas de calibração para pesos estáticos e conhecidos incluem selecionar manualmente percentis da faixa de entrada, otimizar o erro quadrático médio (MSE) entre os pesos originais e quantizados, ou minimizar a entropia (divergência KL) entre os valores originais e os quantizados
Ativações
- As entradas são continuamente atualizadas ao longo do LLM e geralmente são chamadas de "ativações"
- Esses valores mudam sempre que cada dado de entrada é alimentado no modelo durante a inferência, o que torna difícil quantizá-los com precisão
- Como esses valores são atualizados após cada camada oculta, só é possível saber no que eles vão se transformar durante a inferência quando os dados de entrada passam pelo modelo
Part 3: quantização pós-treinamento (PTQ - Post-Training Quantization)
Quantização dinâmica
- As ativações são coletadas depois que os dados passam por uma camada oculta
- Essa distribuição de ativações é usada para calcular os valores de zero-point (z) e fator de escala (s) necessários para quantizar a saída
- O processo se repete cada vez que os dados passam por uma nova camada. Portanto, cada camada tem seus próprios valores de z e s e um esquema de quantização diferente
Quantização estática
- Os valores de zero-point (z) e fator de escala (s) são calculados antecipadamente, e não durante a inferência
- Para encontrar esses valores, um conjunto de dados de calibração é fornecido ao modelo para coletar essas distribuições potenciais
- Ao realizar a inferência real, os valores de s e z não são recalculados; eles são usados globalmente para quantizar todas as ativações
- Em geral, a quantização dinâmica tende a ser um pouco mais precisa, porque tenta calcular os valores de s e z para cada camada oculta, mas isso pode aumentar o tempo de computação
- Já a quantização estática é menos precisa, mas mais rápida porque os valores de s e z já são conhecidos
O território da quantização de 4 bits
- Ir para menos de 8 bits tem se mostrado uma tarefa difícil, porque o erro de quantização aumenta a cada bit perdido
- Exploração de dois métodos comumente compartilhados no HuggingFace: GPTQ e GGUF
GPTQ
- Um dos métodos mais conhecidos atualmente para quantizar em 4 bits
- Usa quantização assimétrica e é aplicado camada por camada, de modo que cada camada é processada de forma independente antes de passar para a próxima
- Durante o processo de quantização por camada, os pesos da camada são primeiro transformados pelo Hessian inverso, que é a segunda derivada da função de perda do modelo e indica o quanto a saída do modelo é sensível a mudanças em cada peso
- Em termos simples, isso mostra a (importância) inversa de cada peso na camada
- Pesos com valores pequenos na matriz Hessiana são mais importantes, porque pequenas mudanças nesses pesos podem causar grandes alterações no desempenho do modelo
GGUF
- GPTQ é um bom método de quantização para executar um LLM inteiro na GPU, mas nem sempre se dispõe dessa capacidade
- Em vez disso, é possível usar GGUF para fazer offload de todas as camadas do LLM para a CPU
- Isso permite usar CPU e GPU ao mesmo tempo quando não há VRAM suficiente
Part 4: treinamento com consciência de quantização (QAT - Quantization Aware Training)
- Na parte 3, vimos como quantizar um modelo após o treinamento, mas essa quantização tem a desvantagem de não considerar o processo real de treinamento
- É exatamente aí que entra o treinamento com consciência de quantização (QAT). Diferentemente do PTQ, o QAT busca aprender o procedimento de quantização durante o treinamento
- O QAT tende a ser mais preciso do que o PTQ, porque a quantização já foi considerada durante o treinamento
A era dos LLMs de 1 bit: BitNet
- BitNet representa os pesos do modelo com um único bit: -1 ou 1
- Isso é feito injetando diretamente o processo de quantização na arquitetura Transformer
- A arquitetura Transformer é usada como base da maioria dos LLMs e consiste em cálculos que incluem camadas lineares
- O BitNet substitui essas camadas lineares por algo chamado BitLlinear
Quantização de pesos
- Durante o treinamento, os pesos são armazenados em INT8 e depois quantizados para 1 bit usando a estratégia básica da função sinal
- Essencialmente, a distribuição dos pesos é deslocada para ficar centrada em 0, e então tudo à esquerda de 0 recebe -1, enquanto tudo à direita recebe 1
Quantização de ativações
- Para quantizar as ativações, o BitLinear usa quantização absmax para converter ativações de FP16 para INT8, porque a multiplicação de matrizes (×) exige maior precisão
Desquantização
- Foram rastreados α (o maior valor absoluto das ativações) e β (o valor absoluto médio dos pesos), e esses valores depois ajudam a desquantizar as ativações de volta para FP16
- As ativações de saída são reescaladas com {α, γ} e desquantizadas de volta para a precisão original
Todos os modelos de linguagem de grande porte têm 1,58 bit
- O BitNet 1.58b foi introduzido para melhorar o problema de escalabilidade mencionado anteriormente
- Nesse novo método, cada peso individual do modelo não pode assumir apenas -1 ou 1, mas agora também 0, tornando-se ternário
- Curiosamente, apenas adicionar 0 já melhora bastante o BitNet e torna o cálculo muito mais rápido
O poder do 0
- Por que adicionar 0 representa uma melhora tão grande? Isso está totalmente relacionado à multiplicação de matrizes
- Quando se têm pesos quantizados em 1,58 bit, não só a velocidade de cálculo aumenta muito, já que basta realizar multiplicações, como também se torna possível fazer filtragem de características
Quantização
- Para realizar a quantização dos pesos, o BitNet 1.58b usa a quantização absmean, uma variação da quantização absmax que vimos antes
- Ela simplesmente comprime a distribuição dos pesos e usa a média absoluta (α) para quantizar os valores, que são então arredondados para -1, 0 ou 1
- Em comparação com o BitNet, a quantização de ativações é igual com uma exceção. Em vez de ajustar as ativações para a faixa [0, 2ᵇ⁻¹], agora se usa quantização absmax para ajustá-las para [-2ᵇ⁻¹, 2ᵇ⁻¹]
- A quantização de 1,58 bit exigiu (principalmente) dois truques:
- adicionar 0 para criar uma representação ternária [-1, 0, 1]
- quantização absmean para os pesos
-
"O BitNet b1.58 de 13B é mais eficiente do que um LLM FP16 de 3B em termos de latência, uso de memória e consumo de energia"
- Portanto, é possível obter um modelo leve porque se usa apenas a quantização computacionalmente eficiente de 1,58 bit
Ainda não há comentários.