1 pontos por GN⁺ 2023-07-24 | 1 comentários | Compartilhar no WhatsApp
  • llama2.c é uma solução “fullstack” de treinamento e inferência que treina a arquitetura LLM do Llama 2 com PyTorch e faz inferência com um simples arquivo C, run.c
  • O foco principal é minimalismo e simplicidade: uma implementação educacional com a arquitetura do Llama 2 hardcoded e composta por um único arquivo de inferência em C puro, sem dependências
  • A partir da ideia de que até LLMs muito pequenos podem ter desempenho forte se o domínio for suficientemente restrito, o projeto fornece modelos de exemplo baseados em TinyStories
    • O modelo de 15M de parâmetros tem download de cerca de 60MB e pode ser executado com make run seguido de ./run stories15M.bin
    • Modelos de 42M e 110M de parâmetros também são fornecidos, e é informado que o modelo de 110M tem o mesmo tamanho do GPT-1
  • Os modelos Llama 2 da Meta também podem ser usados para inferência, pois a arquitetura da rede neural é a mesma, mas é preciso obter o checkpoint seguindo as diretrizes da Meta e depois convertê-lo para o formato do llama2.c com export.py
    • Atualmente, run.c faz apenas inferência em fp32, então modelos maiores que 7B provavelmente serão difíceis de carregar de forma prática
    • Modelos de 13B ou mais não funcionam no momento por causa de integer overflow na aritmética de ponteiros, e isso ainda não foi corrigido
  • A inferência com quantização int8 está implementada em runq.c, usando o esquema Q8_0 para quantizar os pesos que participam do matmul, enquanto os valores de ativação também são quantizados e desquantizados dinamicamente em tempo de execução
    • O export fp32 do Llama 2 7B gera um arquivo de 26GB, enquanto o export quantizado da version 2 gera um arquivo de 6,7GB
    • No ambiente do autor, com OpenMP em 64 threads, o fp32 roda a 4,6 tok/s e o int8 a 14 tok/s, reduzindo o tamanho do checkpoint em 4x e melhorando a velocidade em cerca de 3x
  • O modo de uso inclui execução da inferência em C, entrada de prompt, controle de sampling por temperature e top-p, modo chat e definição de tokenizer customizado
    • Um exemplo de execução é ./run stories42M.bin -t 0.8 -n 256 -i "One day, Lily met a Shoggoth"
    • Modelos de chat são executados com a flag -m chat, como em ./run llama2_7b_chat.bin -m chat
    • Tokenizers customizados podem ser exportados em formato .bin com tokenizer.py e depois informados com a flag -z
  • Modelos no Hugging Face que usam a arquitetura Llama 2 podem ser exportados para arquivos .bin com a flag --hf de export.py
  • O fluxo de treinamento segue download e pré-tokenização do TinyStories, execução de train.py, export do modelo e depois inferência em C
    • O exemplo padrão com TinyStories é atualmente o único exemplo de dataset fornecido
    • O treinamento de tokenizer customizado usa sentencepiece, com o exemplo de configuração --vocab_size=4096
  • Os builds voltados a desempenho são divididos entre make run, make runfast e make runomp; o build com OpenMP ativa #pragma omp parallel for dentro de matmul e attention para dividir o trabalho dos loops entre vários processadores
  • Instruções de build para plataforma são fornecidas para Windows, Centos 7, Amazon Linux 2018 e Mac
    • No Windows, use build_msvc.bat ou make win64
    • Em Centos 7 e Amazon Linux 2018, use make rungnu ou make runompgnu
    • No Mac, o build com OpenMP usa o clang do brew e make runomp CC=/opt/homebrew/opt/llvm/bin/clang
  • Os testes incluem pytest e testes em C com make testcc; test_all.py executa 200 passos de forward em C e Python e compara com a saída esperada conhecida
  • O objetivo do projeto é manter uma implementação de referência simples, composta por 2 arquivos .py de treinamento fáceis de ler e código de inferência em C, sem buscar frameworks complexos nem muitas opções
  • A licença é MIT

1 comentários

 
GN⁺ 2023-07-24
Comentários no Hacker News
  • Vi isso no HN e achei divertido :) O checkpoint original roda a 100 tok/s no MacBook Air M1 compilado com -O3, muito mais rápido do que eu esperava, então agora estou treinando um modelo maior de 44M
    Ainda deve dar para rodar de forma interativa, e talvez até um modelo Llama de 7B esteja ao alcance

    • Fiz uma pequena modificação no nanoGPT para pré-treinar um modelo de 12M com 2GB de dados do TinyStories gerados pelo GPT-4, e o resultado foi bem surpreendente
      Depois adaptei um pouco com Wikipedia, e ele virou um gerador plausível de bobagens muito mais inteligente e muito menor do que um modelo n-gram suavizado. Acho que LLMs pequenos vão se tornar comuns em várias áreas, e o próximo objetivo é reduzir o Llama2 7B para 10~100M sem deixá-lo muito mais burro
    • Como sempre, o trabalho inspira. Pergunta de iniciante: qual seria o caminho mais prático para rodar um LLM de tamanho razoável para um webapp de hobby em um servidor Linux comum, sem GPU sofisticada?
      Alugar uma instância com GPU em algo como Linode é muito mais caro do que um servidor normal para webapp, então queria saber se isso é uma área totalmente inviável ou se uma abordagem como essa, ou algum outro método, é um caminho realista
    • Queria saber se já há alguma impressão inicial sobre a qualidade/desempenho relativo entre modelos pequenos de Llama-2 e modelos pequenos de GPT-2
    • Queria saber se você acha que também daria para fazer um trainer puro em C em vez de Python
    • Queria saber onde esses modelos estão sendo treinados: equipamento em casa, M1 ou nuvem
  • Georgi Gerganov, famoso pelo llama.cpp, tem uma versão rodando no navegador com Emscripten: https://ggerganov.com/llama2.c/
    A thread no Twitter em andamento está em https://twitter.com/ggerganov/status/1683174252990660610
    Tanto o original quanto esse trabalho são realmente muito legais e, embora sejam mais uma prova de conceito usando modelos bem pequenos, os LLMs local-first são especialmente interessantes. Gosto da ideia de poder criar webapps com inferência local
    Se isso evoluir para otimizações, pesquisa em modelos menores, download parcial e uso de WebGPU, pode ser o ponto de partida para uma nova forma de criar apps privados baseados em LLM local. Talvez não chegue às mesmas capacidades de LLMs hospedados em grandes clusters com GPUs de ponta, mas há muitos casos de uso que essa abordagem pode abrir

    • No primeiro link saiu uma saída bem estranha. Começa como um conto de fadas plausível, mas os erros de digitação aumentam e o texto degringola até virar uma mistura de idioma estrangeiro com termos técnicos/de programação
      Por exemplo, começa com “Once upon a time...”, parece a história de Lily e Timmy, mas depois aparecem coisas como “Butterfly would pauseWhy”, “TextField”, querySelector, HttpRequest e fragmentos de vários idiomas, até virar uma saída completamente quebrada
  • Para quem tiver curiosidade, também há uma versão em Rust. Em modo release ela faz cerca de 106 tokens/second
    https://github.com/garrisonhess/llama2.c/blob/517a1a3e487f31...

    • Também há outra versão em Rust. Ela usa a biblioteca candle ML, na qual venho trabalhando desde o mês passado, e também roda no navegador: https://laurentmazare.github.io/candle-llama2/index.html
      A versão não web tem suporte completo a GPU, mas não é nada minimalista
    • Como se vê com frequência em Rust, às vezes alguém simplesmente transpõe algo que já existe só porque é possível, sem trazer benefício algum
      Em alguns casos isso também dispersa os esforços da comunidade para melhorar o projeto
  • Acho que pouca gente entende o quão ousado é esse movimento
    Andrej recebe uma grande remuneração na OpenAI (MSFT), mas ainda assim está ajudando Apple, Facebook e, mais importante, o movimento open source. Mesmo assim seria difícil afastá-lo, porque ele poderia ir direto para a Tesla ou para a xAI

  • Tenho a impressão de que o Llama-2 tem filtragem de segurança pesada demais a ponto de ficar inutilizável para trabalho criativo: https://i.imgur.com/GFY0wSL.png

    • Pessoalmente, acho que a filtragem é tão pesada, em nome da “segurança”, que acaba dando uma volta de 180 graus e reforçando estereótipos odiosos ou negativos: https://i.imgur.com/xkzXrPK.png e https://i.imgur.com/3HQ8FqL.png
      Ainda assim, esse comportamento só foi reproduzido até certo ponto quando enviado como segunda mensagem no TGI Hugging Face do Llama2-70b-chat, e pode haver alguma estranheza na forma do prompt que esteja causando isso. Ainda não pude rodar o modelo por conta própria para investigar mais
    • Se houver um modelo pré-treinado, é melhor não usar modelo instruct/chat
      Chat/instruct tem a vantagem de ser fácil de distribuir para usuários terceiros, os prompts são simples e há mecanismos de segurança, mas para uso próprio ele fica realmente muito atrás de um modelo pré-treinado. Nesse ponto, o Llama 2 pode até ter vantagem sobre a OpenAI, porque a OpenAI parece ter abandonado os modelos pré-treinados GPT-3 e estar seguindo para oferecer apenas modelos de chat no futuro
    • Se você imaginar, seria como Casca e Brutus não apunhalarem César, mas em vez disso o confrontarem educadamente sobre a possibilidade de abuso de poder e suas tendências ditatoriais
    • A filtragem excessiva não é do “llama-2” como um todo, e sim do Llama-2 chat
    • Precisamos afastar o pessoal de “IA ética”. Está ficando cada vez mais claro que eles são realmente irritantes
      Não quero tesoura de segurança. Tudo bem limitar coisas que rodam no próprio servidor deles, mas não deveriam me dar um modelo que eu não possa modificar e usar como quiser no meu computador
  • O Andrej publicou mais detalhes aqui: https://twitter.com/karpathy/status/1683143097604243456?s=46...

  • Como referência, se você tem interesse nessa área, este código compila de forma limpa com o WASI SDK e roda em um runtime Wasm sem modificações

  • Fiquei me perguntando quanta memória é necessária para executar uma rede neural
    Bastaria ler do disco só as duas primeiras camadas e calcular as ativações de todos os nós, depois descartar a primeira camada, ler a terceira, calcular de novo e então descartar a segunda? Se for assim, fico pensando se basta ter memória para armazenar apenas duas camadas

    • O TheBloke, do Hugging Face, documentou os requisitos de memória por versão quantizada dos modelos populares: https://huggingface.co/TheBloke
      Resumindo, o pico de RAM varia conforme o método de quantização, e em linhas gerais fica em 4~8GB para modelos 7B, 8~15GB para 13B, 13~33GB para 30B e 31~75GB para 70B
    • Não é necessário implementar manualmente esse processo de ler e descartar. Basta fazer mmap da rede inteira e deixar o sistema operacional cuidar disso
    • É possível, mas aí você fica limitado pela largura de banda do disco
    • Pelo que entendo, em inferência de transformer O(N²) é preciso manter em cache todas as ativações
  • Pensando aqui: hoje os LLMs retornam uma distribuição de probabilidades, e um amostrador aleatório escolhe uma delas, anexa ao resultado e repete o processo
    Em vez disso, seria possível usar a aleatoriedade para escolher N tokens que aproximem a distribuição, fazer o LLM gerar N novas distribuições e então combiná-las de algum modo, para depois escolher novamente N tokens a partir da distribuição combinada?

    • Isso soa bastante como beam search, e de fato é uma técnica de geração comum: https://en.wikipedia.org/wiki/Beam_search
      Dá para ver um exemplo em https://huggingface.co/docs/transformers/internal/generation...
    • Parece uma linha de pesquisa interessante, mas provavelmente seria preciso gerar muito mais do que 2 tokens à frente. Talvez olhar uns 20 tokens, mas sem querer rodar o LLM N^20 vezes; então talvez fosse melhor selecionar algo como 200 amostras representativas entre as combinações possíveis dos próximos 20 tokens
      Só não sei como fazer isso
    • Sou iniciante, mas gostei da ideia. Não sei a resposta, mas parece algo que daria para testar, e provavelmente algum pesquisador já tentou
      Claro que isso exigiria N vezes mais computação por geração de token. Você pode escolher os N mais prováveis ou, se necessário, aplicar temperatura aos logits para amostrar N deles
  • Isto é para fins educacionais? Pelo sucesso do llama.cpp e deste projeto, parece que a indústria está indo na direção de manter código-fonte separado para cada modelo lançado, em vez de usar frameworks genéricos como PyTorch, TensorFlow e ONNX Runtime

    • Isso parece ser totalmente educacional
      E não. Apesar do nome, o llama.cpp não suporta apenas llama. Também não é algo totalmente sob medida; ele é construído sobre a biblioteca/framework de tensores ggml, que é mais genérica
    • Mesmo dentro dos frameworks, existe código-fonte separado para cada modelo. Isso porque os modelos são código customizado que combina os blocos básicos do framework; eles não são criados apenas com o framework puro. Essa é a natureza da pesquisa exploratória
      Mas, quando se encontra um modelo que funciona bem, esses avanços acabam entrando na versão seguinte do framework. Foi assim que o TensorFlow ganhou blocos básicos como CNN, GRU e TransformerEncoder, ao mesmo tempo em que surgiram implementações específicas para certos hardwares, abrindo mão de generalidade em troca de velocidade
    • Como é single-thread, faz sentido vê-lo como educacional