DFloat11: compressão sem perda para inferência em GPU que reduz LLMs para 70% do tamanho
(arxiv.org)- LLMs grandes são difíceis de implantar por causa dos limites de memória da GPU, e o DFloat11 reduz pesos em BFloat16 para cerca de 70% do tamanho enquanto mantém uma saída idêntica bit a bit à original
- A ideia central é que o expoente de 8 bits do BFloat16 na prática carrega só cerca de 2,6 bits de informação; o sinal e a mantissa são preservados, e apenas o expoente é comprimido com Huffman coding
- Como codificação de comprimento dinâmico tende a virar gargalo na GPU, o DFloat11 usa LUT hierárquica, kernel em 2 etapas e compressão/descompressão por bloco de transformer, adaptado para inferência paralela
- Em modelos como Llama 3.3, Qwen 3, Mistral 3, DeepSeek R1 Distilled, FLUX.1 e Stable Diffusion 3.5, ele mostrou cerca de 30% de redução no tamanho do modelo com preservação da saída original
- Em comparação com a alternativa de CPU offload, a vazão na geração de tokens é 2,3–46,2x maior e permite inferência sem perda do Llama 3.1 405B, com 810GB, em um único nó com 8 GPUs de 80GB
O gargalo de memória que o DFloat11 quer resolver
- Modelos de base como LLMs e Diffusion Models estão crescendo rapidamente, o que dificulta sua implantação eficiente em hardware com restrição de memória
- O Llama 3.1 405B armazena 405 bilhões de parâmetros no formato BFloat16 e precisa de cerca de 810GB de memória para a inferência completa
- Isso excede a capacidade de um servidor GPU avançado típico com 8×80GB de GPU
- Se vários nós passam a ser necessários, o custo de implantação e a barreira de acesso aumentam
- O DFloat11 tem como objetivo comprimir modelos em BFloat16 para cerca de 70% do tamanho original, preservando 100% da precisão em qualquer tarefa
Uma abordagem diferente da quantização com perda
- Quantização é uma forma de compressão com perda que reduz a precisão dos pesos para economizar memória
- Ela pode reduzir bastante o uso de memória e acelerar a inferência, mas introduz erro de aproximação
- A perda de precisão varia conforme o modelo base, o método de quantização, o benchmark de avaliação e o bit-width alvo, então é difícil prever o impacto
- Por exemplo, ao aplicar SmoothQuant de 8 bits ao DeepSeek-R1-Distill-Qwen-1.5B, a precisão média em tarefas de raciocínio cai 9,09%
- Mesmo quando os indicadores gerais de precisão são parecidos, modelos quantizados podem apresentar comportamento de resposta diferente do original
- Dutta et al. observaram o fenômeno de flips, em que uma resposta correta vira incorreta, ou uma incorreta vira correta
- O Qwen2-1.5B quantizado com W8A16 GPTQ tem queda de apenas 0,3% na precisão 8-shot do GSM8K, mas o estado de acerto/erro muda em 6,37% das respostas
- Em áreas como finanças e saúde, mudanças na saída de modelos quantizados podem dificultar o atendimento a exigências de regulação e confiabilidade
- A compressão sem perda existente para modelos costuma focar mais em eficiência de armazenamento de checkpoint, redução do tempo de download em hubs de modelos ou hardware especializado como FPGA, com pouco ganho para inferência em GPU comum
O espaço de compressão ainda disponível no expoente do BFloat16
- O BFloat16 divide 16 bits em 1 bit de sinal, 8 bits de expoente e 7 bits de mantissa
- O ponto de partida do DFloat11 é a análise da entropia de Shannon de cada componente BFloat16 nos pesos de LLMs
- A entropia do sinal e da mantissa fica próxima de seus respectivos bit-widths, então há pouco espaço para compressão
- Já o expoente, embora tenha 8 bits alocados, apresenta entropia de apenas cerca de 2,6 bits
- A distribuição dos valores de expoente é muito desequilibrada
- Dos 256 valores possíveis em 8 bits, só cerca de 40 são usados
- Os demais valores não aparecem
- A frequência também cai rapidamente conforme o ranking
- Graças à baixa entropia, o expoente vira um bom alvo para compressão sem perda, com cerca de 5,4 bits de informação de expoente passíveis de compressão
O formato DFloat11
- DFloat11 ou DF11 é um formato de ponto flutuante de comprimento dinâmico que comprime por entropy coding apenas o expoente de pesos em BFloat16
- A árvore de Huffman é construída com base na distribuição de expoentes dos pesos do modelo
- Valores de expoente mais frequentes recebem códigos curtos
- Valores raros recebem códigos longos
- O sinal e a mantissa são mantidos exatamente como no original
- O expoente é armazenado em forma bit-packed no array de bytes
EncodedExponent - O sinal e a mantissa são armazenados separadamente no array de bytes
PackedSignMantissa
- O expoente é armazenado em forma bit-packed no array de bytes
- Como resultado, pesos em BFloat16 caem para uma média de cerca de 11 bits, e podem ser restaurados ao valor BFloat16 original sem perda de precisão
Descompressão adaptada para inferência em GPU
- Como pesos com entropy coding usam codificação de comprimento dinâmico, eles não podem ser enviados diretamente para multiplicação de matrizes
- A matriz de pesos necessária precisa ser descomprimida imediatamente de volta ao BFloat16 original
- Quando a multiplicação termina, a matriz em BFloat16 é descartada logo em seguida para economizar memória da GPU
- A decodificação Huffman tradicional percorre a árvore sequencialmente no nível de bits, o que não combina com a estrutura paralela da GPU
- Se uma única thread fizer a descompressão, a utilização da GPU cai e a latência aumenta
Decodificação com LUT hierárquica
- O DFloat11 usa decodificação baseada em lookup table em vez de percorrer a árvore de Huffman
- Se o comprimento máximo do código Huffman for L, uma LUT única exigiria tamanho
2^L- Em LLMs, L normalmente fica entre 24 e 32
- Uma LUT com
2^32entradas é grande demais para caber de forma prática na SRAM da GPU
- Para evitar isso, a árvore de Huffman é dividida em subárvores sem sobreposição de altura 8, e cada subárvore vira uma LUT compacta de 256 entradas
- Na LUT hierárquica, algumas entradas precisam atuar como referência para LUTs inferiores
- Isso aproveita o fato de que muitos valores de expoente não são usados nos expoentes de LLMs
- Valores não usados no intervalo 240–255 são reaproveitados como ponteiros internos
- Esses valores representam magnitudes muito grandes, da ordem de ±2^113 a ±2^128, e não aparecem nos pesos de LLMs
- Nos experimentos, o número k de LUTs compactas na árvore de Huffman de expoentes em BFloat16 fica entre 4 e 8
- Junto com
CodeLengths, isso usa no máximo(8 + 1) × 256 bytesde memória - Esse tamanho cabe na SRAM e permite lookups repetidos com alta velocidade
- Junto com
Kernel de GPU em 2 etapas e metadados auxiliares
- Cada thread da GPU fica responsável por um trecho contínuo de n bytes do expoente codificado
- Nos experimentos, foi usado n = 8
- A thread decodifica os códigos Huffman que começam dentro do seu próprio trecho
- A natureza dinâmica do comprimento dos códigos cria dois problemas
- A posição exata do bit inicial de cada thread não é óbvia
- Exceto pela primeira thread, é difícil saber o índice de saída do elemento decodificado
- O primeiro problema é resolvido com o array Gaps
- Gaps tem uma entrada por thread
- Cada entrada indica o offset em bits do primeiro código Huffman válido com base no byte inicial da thread
- Como o comprimento máximo do código é 32 bits, o offset fica no intervalo
[0, 31]e é armazenado em 5 bits
- O problema da posição de saída é tratado armazenando apenas a posição por bloco de threads, reduzindo o overhead de memória
- Se cada thread armazenasse uma posição de saída de 32 bits, o overhead seria alto em matrizes de pesos com dezenas de milhares de threads
- O DFloat11 armazena apenas a posição de saída do primeiro elemento de cada bloco de threads
- O kernel opera em duas etapas
- Na etapa 1, cada thread decodifica seu trecho e apenas conta o número de elementos, sem escrever na HBM
- As threads dentro do bloco executam prefix sum com o algoritmo de Blelloch para calcular a posição de saída por thread
- Na etapa 2, o mesmo trecho é decodificado novamente, e os valores decodificados são escritos no buffer de escrita da SRAM conforme a posição calculada
- O expoente codificado é carregado para a SRAM antes do primeiro passe para evitar acesso duplicado à memória global
- Depois que todos os expoentes decodificados são escritos na SRAM, é feito um único coalesced write para a HBM
Descompressão por bloco de transformer
- A descompressão de uma única matriz de pesos pode ser pequena demais para aproveitar totalmente os recursos da GPU
- À medida que o tamanho da matriz cresce, o throughput de descompressão do DFloat11 melhora
- Descomprimir várias matrizes juntas aumenta o throughput e esconde a latência
- Todas as matrizes de pesos em DFloat11 dentro de um bloco de transformer são descomprimidas como um único batch
- Essa descompressão em lote é feita imediatamente antes do forward pass daquele bloco de transformer
- O token embedding e a language modeling head do LLM também são alvos de compressão
- Essas matrizes já são grandes o suficiente para saturar os recursos da GPU, então não precisam de batching separado
Resultados da avaliação e efeito prático
- O DFloat11 foi avaliado em LLMs e diffusion transformers, incluindo Llama 3, Qwen 3, Mistral 3, DeepSeek R1 Distilled, FLUX.1 e Stable Diffusion 3.5
- Os resultados mostram cerca de 30% de redução no tamanho do modelo com preservação completa da saída original
- A saída é bit-for-bit idêntica à do modelo original
- Como não é compressão com perda, a precisão original dos pesos em BFloat16 é preservada
- Em comparação com a alternativa de descarregar parte de um modelo não comprimido para a CPU para caber na memória, o DFloat11 alcança throughput 2,3–46,2x maior na geração de tokens
- Com um orçamento fixo de memória de GPU, ele permite comprimento de geração 5,7–14,9x maior que o modelo não comprimido
- Embora o Llama 3.1 405B tenha 810GB de tamanho, o DFloat11 viabiliza inferência sem perda em um único nó com 8 GPUs A100 de 80GB
- Esses resultados mostram que é possível reduzir pela metade os requisitos de hardware para rodar o Llama-3.1-405B, mantendo inferência sem perda de accuracy
Ainda não há comentários.