1 pontos por GN⁺ 2025-05-18 | 1 comentários | Compartilhar no WhatsApp
  • KVSplit é um projeto open source que permite executar LLMs grandes e janelas de contexto longas no Apple Silicon
  • Por meio da alocação de precisão separada para chaves e valores, alcança até 72% de redução de memória com perda de qualidade inferior a 1%
  • É otimizado para chips M1/M2/M3 e para o framework Metal
  • Comprova melhorias de velocidade e economia de memória com benchmarks medidos e ferramentas de visualização
  • Oferece instalação fácil, comparação com um único comando e várias ferramentas de benchmark e análise

Introdução: por que o KVSplit é importante

KVSplit é uma ferramenta open source que torna possível executar LLMs de grande porte e janelas de contexto muito mais longas em ambientes Apple Silicon (M1/M2/M3) ao aplicar quantização com precisões diferentes para chaves e valores dentro do cache KV. Ela supera a limitação de projetos anteriores, que quantizavam chaves e valores da mesma forma, e reduz drasticamente o uso de memória enquanto mantém a perda de qualidade em um nível quase imperceptível. Como benchmarks reais, velocidade e métricas de qualidade são todos públicos, é uma solução confiável, e o suporte a Metal junto com ferramentas intuitivas de comparação e visualização aumentam a eficiência de desenvolvimento.

As principais vantagens em relação a projetos semelhantes são:

  • Separação da precisão entre chave e valor para um gerenciamento de memória mais eficiente
  • Especialização para Apple Silicon e otimização completa para o framework Metal
  • Fornece de forma integrada benchmarks, perplexidade, medições de memória/velocidade/qualidade e visualização
  • Instalação com um comando, compatibilidade com modelos e integração com llama.cpp
  • Visualização em tempo real da economia de memória e várias ferramentas de teste comparativo

Principais características e pontos centrais

Visão geral

  • Com KVSplit, é possível executar LLMs muito maiores e contextos mais longos do que antes no Apple Silicon
  • Ao atribuir precisões de quantização diferentes para chaves e valores, obtém tanto economia de memória quanto melhora de velocidade
  • Foram confirmados até 72% de economia de memória, melhora de velocidade (5–15%↑) e perda de qualidade inferior a 1%
  • Suporte completo a Metal, com aceleração ideal no Apple Silicon

Principais resultados de benchmark

  • Na configuração K8V4 (chaves em 8 bits, valores em 4 bits)
    • 59% de economia de memória e 0,86% de perda de qualidade (com base em perplexidade)
    • 5,7% mais rápido que FP16
  • Na configuração K4V4 (chaves/valores ambos em 4 bits)
    • Até 72% de economia de memória
    • Cerca de 6% de perda de qualidade. Recomendado para usos menos sensíveis à qualidade

Comparação do efeito de economia de memória

  • Com base em FP16, 176MB (8K tokens)
    • K8V8 (chaves/valores em 8 bits) : 93.5MB (47%)
    • K8V4 (chaves em 8 bits, valores em 4 bits) : 71.5MB (41%)
    • K4V4 (chaves/valores em 4 bits) : 49.5MB (28%)

Principais funcionalidades

  • Quantização individual de chaves e valores do cache KV
  • Otimização completa para Apple Silicon e Metal
  • Scripts de análise para benchmark/qualidade (perplexidade)/uso de memória
  • Ferramentas de visualização e geração de gráficos com qualidade de publicação
  • Setup com um comando e comparação em tempo real

Estrutura de pastas do projeto

  • llama.cpp: inclui build otimizada para Metal
  • models: armazenamento de arquivos de modelo
  • scripts: inclui scripts de benchmark/instalação/comparação/visualização
  • results, plots: armazenamento de resultados de benchmark e arquivos de visualização
  • README.md

Insights científicos e resultados experimentais

Fenômeno central do cache KV

  • Os vetores de chave têm sensibilidade à quantização muito maior do que os vetores de valor → é preciso atribuir maior precisão às chaves para preservar a qualidade
  • K8V4 é o sweet spot: a combinação chave 8 bits/valor 4 bits oferece o melhor equilíbrio entre qualidade e memória
    • 59% de economia de memória, apenas 0,86% de piora na perplexidade e velocidade superior ao FP16
  • K4V8, mesmo com o mesmo número total de bits de K8V4, apresenta mais de 7 vezes mais perda de qualidade → comprovando a importância da precisão no lado das chaves

Implicações gerais

  • Torna possível obter eficiência de memória e preservação da qualidade do modelo ao mesmo tempo → permitindo operar contextos muito mais longos e modelos maiores em hardware de consumo

Exemplos de uso e recomendações de configuração

Exemplos de execução com diferentes precisões de quantização

  • Padrão (FP16): ./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "prompt" -t 8 --flash-attn
  • Recomendado K8V4: --kvq 8
  • K4V8 (DEMO): --kvq-key 4 --kvq-val 8
  • K4V4 (economia máxima): --kvq 4

Exemplo de contexto longo

  • Contexto de 32K: FP16 requer ~1.4GB, mas K8V4 pode rodar com ~400MB

Explicação das flags de linha de comando

  • -t 8: número de threads, 8 recomendado para M1/M2/M3
  • --flash-attn: otimização para Apple Silicon
  • --kvq N: define N bits para chaves/valores
  • --kvq-key, --kvq-val: suporte a configuração individual
  • -c N: número de tokens de contexto (quanto maior, maior o efeito do KVSplit)
  • -n N: número de tokens gerados
  • -f FILE: arquivo de entrada do documento
  • -m MODEL: caminho do modelo

Benchmark avançado e visualização

  • Com benchmark_kvsplit.py, mede características por configuração, comprimento de sequência, memória/velocidade/perplexidade/escala
  • Com visualize_results.py, gera gráficos em nível de artigo acadêmico
  • Os resultados são salvos automaticamente em CSV/JSON e um resumo é fornecido

Otimização para Apple Silicon e visualização de memória

  • Uso completo do framework Metal
  • Efeito decisivo em modelos M1/M2/M3 com maior pressão de memória
  • Com capture_memory.sh, é possível visualizar a economia de memória em tempo real
  • Devido ao alinhamento de páginas personalizado, a economia real pode diferir ligeiramente do valor teórico

Resumo das funcionalidades

  • Precisão de bits independente para chave/valor e quantização personalizada
  • Otimização completa para Apple Silicon e Metal
  • Benchmark abrangente de memória/velocidade/qualidade
  • Visualização com qualidade de artigo e comparação em tempo real, com suporte a captura de memória
  • Interface de instalação/uso extremamente simples

Citações e pesquisas de referência

  • "More for Keys, Less for Values: Adaptive KV Cache Quantization" (2024)
  • "Unifying KV Cache Compression for Large Language Models with LeanKV" (2025)
  • Implementação base: [llama.cpp], modelo de teste: [TinyLlama]

Recomendações de configuração e planos futuros

  • K8V4 (chaves em 8 bits/valores em 4 bits) : perda de qualidade de 0,86%, redução de memória de 59% e velocidade +5,7%, oferecendo o melhor equilíbrio
  • K4V4: economia máxima de memória (72%), com queda de qualidade de cerca de 6%. Adequado para usos em que memória é prioridade absoluta
  • Especialmente forte para execução com contexto longo, permitindo operar contextos 2–3x mais longos

Roadmap futuro

  • Precisão adaptativa baseada na importância do token
  • Quantização individual por camada
  • Otimização personalizada por modelo (Mistral, Phi-3 etc.)
  • Suporte planejado para demo web e mobile (iOS/iPadOS)

Licença e contribuições

  • Licença MIT
  • Qualquer desenvolvedor ou pesquisador de IA pode contribuir via Issue e PR

1 comentários

 
GN⁺ 2025-05-18
Comentários do Hacker News
  • Demonstra interesse por parecer algo interessante, pergunta sobre a intuição de por que esse fenômeno acontece e como isso foi descoberto, sugere melhorias de usabilidade como o fato de a etapa de aplicar patch no script de instalação estar incompleta e o uso de git submodule, além de propor separar o llama.cpp das dependências Python para considerar diferentes ambientes Python

    • Responde que é uma boa pergunta e explica que a principal diferença está ligada ao papel central da atenção: as chaves determinam em quais tokens focar e formam o padrão de atenção, enquanto os valores apenas transmitem a informação disponível. Destaca que, se a quantização dos vetores de chave for agressiva demais, a distorção aumenta em todas as interações entre tokens, enquanto erros nos vetores de valor afetam apenas a informação daquele token. Usa a analogia de números de prateleiras de biblioteca: se a chave estiver errada, você encontra um livro completamente diferente. Também acrescenta que, matematicamente, as chaves entram no softmax, então pequenos erros podem ser amplificados bastante, enquanto os valores são uma média linear e os erros tendem a se cancelar. Diz que também encontrou essa assimetria em um artigo e quis verificar quantitativamente seu impacto no Apple Silicon, prometendo melhorar o feedback de instalação e as dependências Python
    • Aponta que o patch na prática não é aplicado ao llama.cpp, que o código de parsing de argumentos já foi movido para arg.cpp e por isso o patch não faz sentido, e explica que as opções de quantização K/V já haviam sido adicionadas ao llama.cpp em 2023. Questiona a razão de existir do patch, dizendo que parece apenas mudar os argumentos de linha de comando, e recomenda fortemente evitar executar um arquivo install.sh de um novo repositório para aplicar um patch tão simples
  • Pergunta se esse patch também seria possível no MLX, observando que o MLX parece mais rápido e esperando que essa abordagem ajude usuários de Mac com conversas longas em velocidade prática de uso real

    • Responde que provavelmente é possível, mas que está justamente mergulhando no MLX, e que o framework em si é bem projetado, embora ainda pareça imaturo por falta de código de exemplo e informações de benchmarking. Comenta até que o binding para Haskell é o que mais lhe anima, por achar que as características de lazy evaluation podem combinar bem e que a estrutura puramente funcional do grafo de compilação também pode encaixar, expressando vontade de ver ML em Haskell
  • Pergunta se há diferença prática em relação às opções --cache-type-k e --cache-type-v

    • Opina de forma contundente que isso parece apenas uma tentativa de conseguir estrelas no GitHub e que, na prática, não difere de uma funcionalidade já existente
    • Lembra que no MLX/MPS não havia suporte a 4 bits, ou que até 8 bits ainda era insuficiente, e que o suporte a bf16 foi adicionado recentemente. Por isso, no passado, em GPUs da Apple, a abordagem type_k/v só permitia no mínimo 16 bits (f16/bf16), então, embora não conheça bem os detalhes internos do llama.cpp, supõe que possa haver alguma diferença
    • Concorda de forma breve que também tem curiosidade sobre essa diferença
  • Depois de ler o código, conclui que o patch é desnecessário e confirma por um link de PR que essa funcionalidade já foi incorporada ao llama.cpp em 2023. Diz que é preciso cautela porque o projeto induz o usuário a executar um install.sh para aplicar patch em vez de usar um fork do repositório, aponta uma estrutura confusa com vários arquivos de patch, código duplicado e sobrescrita de patches, e observa que na prática só adiciona a opção --kvq, embora já existam opções separadas para quantização K/V. Suspeita que o autor não poderia desconhecer essa funcionalidade existente, não recomenda executar scripts de repositórios com lógica complexa, critica o fato de o post no HN e o número de estrelas no GitHub estarem altos apesar de o conteúdo ser enganoso, preocupa-se com o fato de o autor continuar evitando perguntas e conclui que tanto o repositório quanto o script estão misturados com uma base antiga do llama.cpp e não batem com a estrutura atual, o que aumenta a confusão

    • Diz que também viu vários pontos suspeitos, mas ficou esperando a explicação do autor caso tivesse deixado passar algo. Ainda assim, aponta vários sinais de alerta e, ao olhar o histórico de atividade no GitHub, suspeita de uma intenção de inundar projetos populares com código gerado por LLM
    • Comenta que finalmente apareceu alguém falando de forma racional, diz que o simples fato de o projeto usar patch em vez de ser um fork já representa um risco de confiança, considera suspeita até a própria presença do autor no GitHub e aponta uma tendência de enviar em massa PRs com resíduos de LLM para projetos populares para parecer colaborador, expressando preocupação com poluição informacional e com o início de um colapso de confiança causado por IA
  • Pergunta se também é possível aplicar quantização KV diferenciada (K8V4 etc.) a modelos já convertidos em formato .gguf, e se há restrições ligadas ao tipo de modelo ou configuração de tokenizer

    • Responde que a principal vantagem do KVSplit é justamente poder ser usado diretamente com modelos .gguf existentes, sem conversão especial. Explica que a quantização é aplicada apenas ao cache KV em tempo de execução e é independente dos pesos do modelo, e que os flags --kvq-key e --kvq-val apenas definem o formato de armazenamento em memória. Diz que testou com sucesso em vários modelos, como LLama-3, Mistral, Phi-2/Phi-3, TinyLlama e Qwen, mas ressalta que isso é exclusivo do backend Metal do llama.cpp e que o Flash Attention atualmente ignora formatos customizados de cache KV, então é preciso usar a opção -fa 0. Fora isso, afirma que funciona com qualquer arquitetura Transformer que use atenção
  • Pergunta se, em Apple Silicon com muita memória, como 64 GB ou 128 GB, esse patch fica mais rápido ou melhor, comentando rumores de que ampliar a janela de contexto no Apple Silicon na prática fica lento e questionando se isso tem utilidade real em máquinas com muita memória

    • Responde que o ganho de economia de memória do KVSplit é proporcional ao comprimento do contexto, então quanto mais RAM o Mac tiver, maior será a vantagem absoluta. Diz que em um Mac Studio de 128 GB é possível lidar com contextos de centenas de milhares de tokens, mas que isso não acelera fundamentalmente o cálculo, apenas melhora a eficiência de memória. Cita benchmarks com aumento de throughput de 14,5% na configuração K8V4, atribuído à eficiência de acesso à memória. Explica que o problema de “lentidão” com modelos grandes vem principalmente do limite de desempenho computacional e não de RAM ou da presença de otimizações no cache KV; assim, o KVSplit é útil quando o gargalo é memória. Em uso prático, considera ideal alocar janelas de contexto maiores para modelos menores, como 7B a 13B. Se forem necessários ao mesmo tempo modelos grandes e contextos extremamente longos, GPUs de nível servidor ainda são mais apropriadas, mas a técnica amplia o limite do que dá para fazer no hardware da Apple
  • Demonstra interesse e pede uma explicação mais de alto nível: pergunta se um modelo de 2048 tokens pode ser estendido para algo como 4~6k, se um modelo com contexto de 128k poderia ser usado com janelas de 256k+, e quais seriam os casos de uso ideais para modelos locais

    • Responde que, com a configuração K8V4, é possível reduzir o uso de memória em 59%, o que aumenta o comprimento máximo de contexto em até 2,4 vezes. Assim, um modelo de 2048 tokens poderia ir para cerca de 5000 tokens e um modelo de 8K para algo próximo de 19,5K. Diz que, na prática, isso permite processar um livro inteiro de uma vez no MacBook, analisar bases de código grandes ou manter histórico longo em apps conversacionais. Explica que a economia cresce linearmente com o tamanho do contexto e relata que, em sua experiência, o cache KV caiu de 176 MB para 72 MB em contexto de 8K, enquanto em 128k a economia chega a vários gigabytes. Afirma que essa é uma solução especialmente eficaz quando o problema é OOM por limite de comprimento de entrada
    • Diz que, como a economia de memória reduz os recursos exigidos por determinado modelo, a técnica pode ser aplicada de várias formas conforme o objetivo. Ainda assim, ressalta que ampliar a própria janela de contexto não é algo simples para não especialistas, então é melhor usar modelos treinados para janelas maiores. Observa que modelos locais podem ser usados para fins offline, segurança ou experimentação, e que, na maioria dos casos, são usados em testes de ajuste de modelos
  • Elogia a ideia e a tentativa, pergunta se isso também poderia ser aplicado em GPU e se pode ser combinado com outras técnicas de quantização

    • Responde que isso provavelmente pode ser aplicado da mesma forma em GPUs NVIDIA/AMD, porque o princípio de que as chaves exigem mais precisão que os valores é independente de hardware. Observa que o backend CUDA do llama.cpp já permite configuração separada com --cache-type-k e --cache-type-v, e que o patch atual é especializado para Metal, mas o princípio pode ser portado como está. Também afirma que pode ser combinado com outras técnicas de quantização, já que a quantização do cache KV só é aplicada durante a execução e não conflita com a quantização dos pesos, sendo uma etapa separada no pipeline. Ressalta, porém, que engines como vLLM e TensorRT-LLM, que usam estruturas próprias de cache, exigiriam implementação específica. Acrescenta que, em GPU, o maior efeito viria ao integrar isso diretamente à arquitetura do FlashAttention, onde economia de memória pode se converter em ganho de velocidade
  • Diz que há partes que não entende bem e que algo parece estranho, recomenda evitar executar o script em questão e informa que fez uma denúncia

  • Pergunta como o desempenho muda: mesmo colocando um contexto mais longo na memória, a velocidade de computação no fim não seria a mesma?

    • Comenta que, na prática, ao testar o mesmo modelo e o mesmo prompt com tipos de cache fp16, q8 e q4, sentiu velocidades de iteração parecidas. Diz que não verificou o funcionamento interno, mas esperava que os vetores fossem empacotados com processamento SIMD em lote de 4 a 8 bits, embora tenha ficado com a impressão de que esse empacotamento na verdade não ocorre