Weight Folding, Streams CUDA e o Bug Que Fez Meu Modelo Falar ao Contrário — Filip Makraduli

AAI Engineer
컴퓨터/소프트웨어AI/미래기술

스크립트

00:00:00.
00:00:13Olá a todos, obrigado por virem e
00:00:18vou começar a palestra agora. Então
00:00:22esta palestra gira em torno de um artigo
00:00:27que fiz, que é muito simples.
00:00:33A proposta é muito clara.
00:00:36Basicamente, são duas linhas de
00:00:39álgebra que tornam a camada de normalização RMS
00:00:42em transformers mais barata,
00:00:45mais rápida e que de certa forma a melhoram
00:00:48como uma camada na arquitetura
00:00:51transformer. De forma semelhante a como
00:00:54a normalização de camada costumava ser o padrão
00:00:57e depois foi substituída pela normalização RMS.
00:01:00Isso segue essa mesma linha de raciocínio.
00:01:04E tive a oportunidade de conhecer
00:01:09algumas pessoas do mundo de código aberto
00:01:11e co-criei este artigo
00:01:14junto com Nils Graf, que foi
00:01:17o criador disso.
00:01:21E o trabalho continua a partir daí.
00:01:23Portanto, isto está publicado no arXiv.
00:01:26Vocês podem dar uma olhada, ler, testar.
00:01:29Há também um repositório.
00:01:31E o conceito, digamos, a ideia
00:01:36e a forma de pensar são mais fáceis
00:01:38de explicar usando talvez o FlashAttention.
00:01:40De forma semelhante a como o FlashAttention
00:01:45espera até haver uma multiplicação
00:01:48e tenta limitar essa comunicação
00:01:52entre memórias para que todo o processo
00:01:54seja mais rápido.
00:01:55Este é um pensamento semelhante nessa mesma linha.
00:01:58E faz certas melhorias
00:02:01que tornam o processo da normalização RMS muito mais rápido
00:02:08e, por efeito, melhoram todo o transformer.
00:02:15E uma pergunta é: ok, porquê a normalização RMS?
00:02:19Uma vez que essa camada quase não faz contas.
00:02:24E isso é verdade.
00:02:26Portanto, a fatia da parte matemática,
00:02:29se olharmos para ela, é bastante pequena.
00:02:31No entanto, o tempo de relógio ou tempo de execução,
00:02:34como costumam dizer, é bastante grande.
00:02:36E, por exemplo, num passo de descodificação,
00:02:40ou seja, logo quando a inferência é executada,
00:02:43a normalização RMS pode ser iniciada umas 33 vezes.
00:02:47Claro que depende do modelo e assim por diante.
00:02:50No artigo, têm os modelos específicos
00:02:52e como isto foi testado.
00:02:54E a questão é como isto pode ser melhorado
00:02:59e como esta espera pela multiplicação de matrizes
00:03:03pode ser de certa forma evitada.
00:03:06E a razão pela qual isto é lento
00:03:09é porque as GPUs não são lentas nem fracas a fazer contas,
00:03:15mas são fracas em tudo o resto à volta das contas reais.
00:03:20Ou seja, iniciar o trabalho, o trabalho real.
00:03:24Portanto, por exemplo, iniciar o processo como acontece
00:03:30em algumas das experiências, 33 vezes,
00:03:32isso demora muito tempo.
00:03:35E, por exemplo, fundir cada normalização
00:03:40na multiplicação de matrizes pode ajudar a evitar isto.
00:03:44Além disso, fazer a fusão de pesos
00:03:46pode ajudar a mover dados entre a memória,
00:03:50e esse é um processo que também é lento para as GPUs.
00:03:54E também a espera.
00:03:56Portanto, por exemplo, adiar a divisão
00:04:00que é feita na camada de normalização RMS
00:04:02é também uma forma de evitar este passo de espera.
00:04:06Basicamente, o que este artigo faz
00:04:09é melhorar estes três aspetos
00:04:13ao fazer alguns truques algébricos
00:04:16na forma como a normalização RMS é computada.
00:04:19É isso.
00:04:21E, em termos matemáticos, estes são os truques.
00:04:25Centram-se principalmente nas duas primeiras proposições.
00:04:29Uma é a normalização sem pesos.
00:04:31Podem ver isso aqui.
00:04:33E a normalização adiada.
00:04:35Portanto, essa é a segunda.
00:04:37E agora, em arquiteturas mais recentes,
00:04:39existe uma situação em que o RMS pode aparecer duas vezes.
00:04:44Por exemplo, no Gemma 4 isto acontece.
00:04:48Portanto, cancelar a pré-normalização também funciona.
00:04:52E tudo isto é provado algebricamente no artigo.
00:04:58E a primeira proposição é esta,
00:05:00onde o ganho e a fusão de pesos
00:05:04se fundem numa única matriz.
00:05:06O W que podem ver aqui com um asterisco.
00:05:09E isso é computado fora de linha (offline),
00:05:12de forma semelhante a como no FlashAttention
00:05:14se computam algumas coisas paralelamente
00:05:16para que não haja comunicação
00:05:18entre a memória a toda a hora.
00:05:20Portanto, este é um passo que é feito, por assim dizer,
00:05:24esta fusão de pesos.
00:05:26E o outro passo é adiar a divisão por escalar
00:05:32da multiplicação de matrizes para que possam ser feitas em paralelo.
00:05:35Portanto, num caso normal, teriam de computar uma vez,
00:05:38depois esperar e computar novamente.
00:05:41Neste caso, a ideia é dividir isto
00:05:44para que possa ser paralelizado.
00:05:48E a terceira, que é uma espécie de versão disto,
00:05:51é que existe, caso haja duas,
00:05:56como isto é invariante à escala,
00:05:58uma delas pode ser descartada e continua a funcionar.
00:06:01E isto é aplicável a modelos mais recentes
00:06:04que possam ter esta arquitetura e implementação.
00:06:10Portanto, para tornar isto realidade,
00:06:13especialmente esta proposição número dois.
00:06:16Portanto, para esta, por exemplo, é fácil.
00:06:20Há um repositório chamado Transformer Tricks.
00:06:22Podem simplesmente aplicar isto a qualquer modelo e funciona.
00:06:25Mas para o fazer, há algum trabalho ao nível do kernel.
00:06:29Portanto, não é assim tão simples de fazer.
00:06:31Então, para eu conseguir fazer isso,
00:06:35estive a implementar isto e deparei-me com esta experiência.
00:06:42Parece bem em geral, onde é tipo,
00:06:46ok, o prompt é a arquitetura transformer
00:06:48revolucionou o PLN porque,
00:06:51e depois há algum tipo de saída esperada.
00:06:54Mas na saída que obtive,
00:06:56vi esta repetição e um atraso de um passo.
00:06:59Como podem ver aqui, a palavra porque aparece novamente.
00:07:01E algo estava a acontecer com os fluxos da GPU
00:07:07e eu estava a tentar perceber o que se passava.
00:07:10E estava a obter este atraso de um passo e saídas
00:07:15que eram do passado, de certa forma.
00:07:18E ao depurar tudo isto,
00:07:21percebi que no processo de construir algo assim...
00:07:26Como expliquei na proposição dois, ou ao adiar isto para operações,
00:07:32em CUDA, podem fazer duas coisas.
00:07:35Podem usar núcleos de tensor (tensor cores) que fazem uma parte da multiplicação de matrizes
00:07:39e podem usar núcleos CUDA que executam coisas como operações elemento a elemento,
00:07:44reduções, raízes quadradas, etc.
00:07:47Portanto, a ideia era fazer isto em paralelo e obter o benefício
00:07:52do que estava a explicar no artigo para testar realmente este conceito.
00:07:58Então, era para ser mais ou menos assim.
00:08:00Então, se você faz as coisas sequencialmente,
00:08:03há um tempo de espera ocioso quando a unidade vetorial calcula o RMS e o escalonamento,
00:08:10e então ocorre a multiplicação de matrizes.
00:08:12A ideia era, ok, com o flash norm, que é a técnica do artigo,
00:08:16você deve fazer ambas as coisas em paralelo.
00:08:19Assim, a unidade matricial calcula a multiplicação e a unidade vetorial calcula o RMS.
00:08:23Dessa forma, você economiza tempo.
00:08:26No entanto, você não pode simplesmente fazer isso em Python.
00:08:28Você precisa ir um pouco mais a fundo.
00:08:30E eu fiz isso com códigos CUDA como este.
00:08:35E isso parecia, no geral, OK naquele momento.
00:08:42No entanto, percebi que fiz algo ligeiramente errado.
00:08:47E o problema foi que a junção no final, onde você deveria unir os dois fluxos, era implícita no meu caso.
00:08:57E quando testei isso, o teste unitário funcionou.
00:09:01A qualidade parecia semelhante, como testes de perplexidade e assim por diante, porque é apenas uma geração parecida.
00:09:08Mas, ao gerar textos longos, consegui notar esse problema.
00:09:11Então eu não fazia ideia do que era aquilo.
00:09:14E o motivo era que, ao fazer essa junção implícita, basicamente um dos fluxos ainda não havia terminado o trabalho.
00:09:24Portanto, tive condições de corrida que acabavam lendo dados antigos da multiplicação de matrizes inacabada.
00:09:31Então a ideia que tive para corrigir isso girava em torno do fato de que eu precisava ser explícito sobre a junção.
00:09:40E esperar até que uma das operações termine para ter certeza de que, ao unir, não estou lendo dados antigos.
00:09:48Essa foi a conclusão tirada dessa exploração dos fluxos CUDA.
00:09:54E era assim que eu estava fazendo as coisas.
00:09:57A junção era implícita.
00:10:00Portanto, a etapa de pós-escala lia um valor de buffer antigo.
00:10:06E a forma de corrigir isso é marcando o fim da multiplicação de matrizes,
00:10:14depois marcando o fim do RMS.
00:10:17E então, na pós-escala, aguardar pelo primeiro fluxo.
00:10:21E em seguida, aguardar pelo segundo fluxo.
00:10:24Isso corrigiu o bug e fez o artigo funcionar, fazendo o modelo falar para frente em vez de para trás.
00:10:33Essa foi talvez a perspectiva acadêmica mais legal.
00:10:38Mas eu também queria testar as coisas, certo?
00:10:40Implantar isso, testar, ver como posso fazer funcionar em um ambiente mais voltado para produção.
00:10:47E você também pode ler o artigo e ver todos os testes.
00:10:50Alguns foram feitos, a maioria com modelos LLaMA, mas isso funciona para outras arquiteturas também.
00:10:56Então, o que você pode fazer para este artigo específico é, por exemplo, a dobra de pesos que expliquei, a proposição um, você pode fazer apenas com um código no repositório.
00:11:10É como o flash, você diz flashify e ele faz isso.
00:11:13No entanto, com essa segunda coisa que mencionei, você precisa trabalhar um pouco com kernels se quiser fazer aquilo.
00:11:19Assim como expliquei no meu exemplo.
00:11:22E esses são alguns resultados baseados em modelos LLaMA, e há diferentes tipos de detalhes que você também pode conferir.
00:11:29Como o que acontece se você fizer apenas normalização adiada, o que acontece se você fizer um kernel totalmente fundido.
00:11:36Portanto, há muitos experimentos indo a níveis mais baixos aqui para testar todas as proposições.
00:11:41E estes têm sido os nossos resultados em diferentes, digamos, níveis de escrutínio e detalhe.
00:11:48Mas mesmo o mais simples, como a dobra de pesos, mostra alguma melhoria.
00:11:54E isso também funciona com as ferramentas do dia a dia que você usa em um modelo.
00:11:59Então não é como se você precisasse reinventar a roda ou, sabe, fazer as coisas do zero.
00:12:05Funciona com o torch compile porque é basicamente como um novo ponto de verificação e pronto.
00:12:13O FlashAttention faz truques semelhantes em uma camada diferente.
00:12:16E também funciona com modelos quantizados.
00:12:18Portanto, é super legal aplicar isso e você pode obter um modelo que possui essa nova e interessante camada de normalização.
00:12:27E onde você pode encontrar esses detalhes e códigos para rodar isso é no repositório transformer tricks.
00:12:34Ele contém diferentes truques de álgebra que expliquei, assim como o artigo que mencionei.
00:12:41E há também o repositório de modelos do Hugging Face, onde fiz isso com alguns modelos.
00:12:50E você pode ter um link do Hugging Face para esse modelo e testá-lo.
00:12:54E o que você também pode fazer com esses modelos do Hugging Face é implantá-los em produção.
00:13:00Então, quando pensei em fazer isso, percebi que, ok, agora que digamos que a ciência está pronta e há um link para um modelo do Hugging Face,
00:13:11o motor de inferência Superlink era uma maneira legal de implantar qualquer modelo do Hugging Face.
00:13:19E fizemos isso em hackathons onde as pessoas traziam um modelo personalizado do Hugging Face ou um checkpoint com suas coisas ajustadas.
00:13:27E você pode testar, mesmo se tiver alguma versão desses truques algébricos que queira usar para melhorar um modelo e testar suas próprias ideias de pesquisa,
00:13:37você pode realmente experimentar isso e ter uma versão implantada desse modelo em um cluster, sem precisar se preocupar com códigos de integração para implantar modelos.
00:13:48Isso é bem legal. E o ponto é que, se você tem o cluster completo de código aberto e a inferência de modelos de código aberto,
00:13:58você realmente pode testar esse tipo de ideia de pesquisa mais inovadora, onde, se você quiser fazer manipulação de kernel ou flash norm e coisas assim,
00:14:12é muito mais difícil fazer isso em um endpoint alugado onde você não é dono da inferência.
00:14:18Então, você quer algo que seja portátil e flexível para permitir que faça essas coisas,
00:14:23mas que também seja pronto o suficiente para produção para que você possa testar as coisas em escala.
00:14:27E você pode, por exemplo, usar o Cy para combinar isso com outros modelos.
00:14:33Como você pode ver no canto superior esquerdo, você pode ter esses modelos flashificados combinados com outros modelos para realizar tarefas de agentes, se quiser,
00:14:43e fazer aquele caso de uso maior de ponta a ponta.
00:14:46E a forma como o Cy funciona é que este cluster de produção ajuda você a implantar os modelos.
00:14:52Portanto, você também pode dar uma olhada no repositório do Cy para mais detalhes sobre isso.
00:14:57E há também um mecanismo de fila mais inteligente que ajuda você, especialmente se trabalhar com modelos menores,
00:15:03porque ao fazer as coisas do flash norm, eu trabalhei com modelos LLaMA menores e também com pequenos agentes do Hugging Face.
00:15:11Então, ter uma maneira de implantar modelos menores que também possam rodar na mesma GPU, para que você não precise gastar seu dinheiro com custos de GPU,
00:15:24mas possa alternar os modelos, especialmente os menores, foi bastante útil.
00:15:30E você também pode controlar as configurações do modelo por meio de uma API, bem como o cluster,
00:15:35o que também é bastante conveniente, sem precisar de um especialista em infraestrutura apoiando sua pesquisa de código aberto.
00:15:40Então isso também é legal.
00:15:43E você é dono da sua nuvem, o que é útil se você quer pesos abertos, modelos abertos e código aberto.
00:15:50E há também um catálogo que o Cy possui com diferentes modelos, não apenas os que mencionei,
00:15:57mas você pode dar uma olhada.
00:15:59Há também modelos de re-ranking e embedding, caso você esteja construindo algo nessa linha.
00:16:03E com isso, encerro esta história da minha jornada de pesquisa, onde co-lumperei este artigo sobre a técnica que melhora o transformador,
00:16:15mas também encontrei uma forma de levar isso para a produção, testar e achar um jeito de brincar com esses modelos de código aberto.
00:16:24E fiquem à vontade para me contatar no LinkedIn, caso tenham alguma dúvida ou queiram contribuir.
00:16:30Muitas das coisas que mencionei, algumas delas são PRs no VLLM ou no Hugging Face.
00:16:36Você pode encontrá-las por aí.
00:16:38Você também pode conferir o artigo.
00:16:40Esse é o link do arXiv que está aí.
00:16:43E você também tem o repositório do Cy e o meu LinkedIn.
00:16:47Portanto, muito obrigado pela participação.
00:16:58E vocês podem me encontrar para perguntas.
00:16:59Estaremos por aqui.
00:17:00Bem perto.
00:17:13você

핵심 요약

O artigo Transformer Tricks reduz a sobrecarga de computação da normalização RMS através de truques algébricos e manipulação explícita de fluxos CUDA.

하이라이트

  • A camada de normalização RMS em transformers é otimizada por meio de duas linhas de álgebra que fundem pesos e adiam divisões.

  • A normalização RMS pode ser executada até 33 vezes num único passo de descodificação durante a inferência do modelo.

  • A junção implícita de fluxos CUDA causa condições de corrida que provocam atrasos de um passo e leitura de dados antigos na GPU.

  • O repositório Transformer Tricks fornece implementações em código CUDA para testes práticos das proposições algébricas.

  • O motor de inferência Superlink viabiliza a implantação de modelos personalizados e testes de pesquisa em clusters de código aberto.

타임라인

Otimização algébrica da normalização RMS

  • A camada de normalização RMS consome muito tempo de execução apesar da simplicidade matemática.
  • A normalização RMS ocorre cerca de 33 vezes num único passo de descodificação durante a inferência.
  • Truques algébricos reduzem a comunicação entre memórias e aceleram o processo do transformer.

A proposta do artigo consiste em aplicar alterações algébricas para tornar a normalização RMS mais rápida e barata. As GPUs perdem tempo ao iniciar tarefas repetidas vezes e ao mover dados entre memórias. Três proposições matemáticas abordam a normalização sem pesos, a normalização adiada e o cancelamento da pré-normalização em arquiteturas recentes.

Resolução de bugs em fluxos CUDA

  • A paralelização entre núcleos de tensor e núcleos CUDA exige sincronização explícita de fluxos.
  • A junção implícita de fluxos CUDA gera condições de corrida e leitura de dados antigos de buffers.
  • A marcação explícita do fim das operações corrige o problema e restaura a direção correta da geração de texto.

A implementação prática da normalização adiada utiliza núcleos de tensor para multiplicações de matrizes e núcleos CUDA para operações elemento a elemento em paralelo. Uma junção implícita entre os fluxos resulta em leituras incorretas de dados antigos e texto repetido. A correção exige aguardar explicitamente pelo término de cada fluxo individualmente antes da etapa de pós-escala.

Implantação e testes em produção

  • O repositório Transformer Tricks e Hugging Face disponibilizam códigos e modelos para experimentação.
  • O motor de inferência Superlink facilita a implantação de modelos personalizados em clusters de código aberto.
  • A gestão inteligente de filas em modelos menores otimiza custos com recursos de GPU em ambientes de pesquisa.

A aplicação prática das técnicas funciona com ferramentas padrão como torch compile e modelos quantizados. O uso de infraestrutura própria de código aberto elimina a dependência de endpoints alugados. Mecanismos de filas permitem alternar modelos menores na mesma GPU para viabilizar testes complexos de agentes.

커뮤니티 글

아직 글이 없습니다. 이 영상에 대한 첫 번째 글을 작성해 보세요!

이 영상에 대해 글쓰기