2 pontos por GN⁺ 2024-02-05 | 1 comentários | Compartilhar no WhatsApp
  • Depois de treinar um Transformer decoder-only com cerca de 10 milhões de parâmetros em TinyShakespeare, o autor rastreou, por meio de estados internos e experimentos, como o resultado do cálculo após a autoatenção se transforma em probabilidades do próximo token
  • A hipótese central é que cada bloco do Transformer conecta o prompt a um conjunto específico de strings do corpus de treino, e que a distribuição de frequência dos tokens que realmente aparecem após essas strings se aproxima da previsão de próximo token do bloco
  • O procedimento de aproximação implementado encontra strings de treino semelhantes à saída da feed-forward network de cada bloco, soma a distribuição dos caracteres que vieram em seguida e então reconstrói a saída do modelo com uma soma ponderada por bloco
  • Em 20.000 prompts de comprimento 10, a distância de Hellinger média entre a saída aproximada e a saída real do Transformer foi de cerca de 0,17; a distância entre o modelo original e modelos alternativos treinados com a mesma arquitetura, mas seeds diferentes, foi de cerca de 0,11 ± 0,08
  • A análise sustenta a interpretação de que a saída da feed-forward tem grande peso na transformação do bloco e que a attention mapeia o prompt para a classe correta de strings de treino, mas não está claro se isso se generaliza além de modelos pequenos

Modelo analisado e a pergunta

  • Foi implementado e treinado um Transformer com cerca de 10 milhões de parâmetros seguindo Andrej Karpathy em Let’s build GPT: from scratch, in code, spelled out
    • O código principal do modelo de linguagem é trabalho de Karpathy; a análise interna e o código auxiliar são contribuições separadas
    • O código relacionado está em transformer-experiments
  • O modelo é um Transformer decoder-only composto por 6 blocos
    • Os dados de treino são TinyShakespeare, com 40.000 linhas de peças de Shakespeare
    • Após cerca de 1 hora de treino em uma GPU RTX 4000, ele gera texto falso no estilo Shakespeare que parece estruturalmente plausível
  • Os tokens são caracteres, não palavras
    • O vocabulário tem 65 itens, e o modelo prevê a distribuição de probabilidade do caractere que vem depois do prompt de entrada
    • Por exemplo, após 'my most gr', ele prevê o próximo caractere como a com 0,819, e com 0,081 e i com 0,059
  • A pergunta central não é sobre a autoatenção em si, mas sobre como o resultado após a attention se transforma na probabilidade final do próximo token

Estrutura dos blocos e o peso da feed-forward network

  • Cada bloco do Transformer inclui multi-head self-attention e uma feed-forward network
    • Na estrutura em PyTorch, a saída do bloco tem a forma x + self.sa(self.ln1(x)), seguida por x + self.ffwd(self.ln2(x))
    • Em variáveis intermediárias, a saída do bloco pode ser vista como x + sa_out + ffwd_out
  • No modelo analisado, a feed-forward network responde por 65,71% de todos os parâmetros treináveis
    • Parâmetros da feed-forward: 7.089.408
    • Total de parâmetros treináveis: 10.788.929
  • A saída da feed-forward network parece ser o principal fator na conversão da entrada do bloco em sua saída
    • Nos experimentos posteriores também, a saída da feed-forward mostrou mais influência na distribuição final de probabilidade do que a saída da self-attention isoladamente

Encontrando strings por saídas feed-forward semelhantes

  • O procedimento de aproximação usa a similaridade da saída feed-forward entre o prompt e strings do corpus de treino
    1. O prompt é inserido no modelo e a saída da feed-forward network de cada bloco é armazenada
    2. Para cada bloco, busca-se no corpus de treino strings com saída feed-forward semelhante
    3. Soma-se a distribuição de frequência dos tokens que vieram depois dessas strings
    4. As distribuições por bloco são combinadas com pesos e normalizadas para formar uma distribuição de probabilidade
  • No prompt de demonstração 'And only l', foram encontradas 94 strings de comprimento 10 com similaridade de cosseno acima de 0,95 com a saída da feed-forward do bloco 1
    • As strings encontradas geralmente terminavam em y l ou ly l, como hat only l, \nMy only l, I dearly l, ng Henry l, And only l
    • A distribuição de frequência normalizada dos caracteres que vinham depois dessas strings foi muito semelhante à previsão real do modelo para o próximo caractere
  • No exemplo 'And only l', a aproximação e a previsão do modelo real ficaram próximas
    • Modelo: i 0,437, o 0,204, a 0,195, e 0,160
    • Aproximação: i 0,389, o 0,250, a 0,222, e 0,139
    • A distância de Hellinger foi 0,0711
  • Há casos em que um único bloco falha
    • No prompt 'hing tremb', usando apenas o bloco 1, o modelo prevê l com 0,999, mas a aproximação dá e 0,543 e l 0,343, produzindo uma distância de Hellinger de 0,6305
    • Quando todos os blocos são considerados, no mesmo exemplo a aproximação prevê l com 0,997 e a distância de Hellinger cai para 0,0233

Escala computacional e ajuste manual

  • Por eficiência, toda a análise foi feita principalmente com strings de comprimento 10
    • O corpus de treino tem 1.115.394 caracteres, e há 858.923 substrings únicas de comprimento 10
    • A saída feed-forward é um vetor float32 de 384 dimensões, um por cada um dos 6 blocos
  • 20.000 strings de comprimento 10 foram escolhidas aleatoriamente e usadas como prompts de teste
    • A similaridade de cosseno por bloco entre cada prompt e as 858.923 substrings únicas foi pré-calculada e salva em disco
    • Como não apareceram correspondências relevantes abaixo de similaridade 0,7, foi aplicado um pré-filtro mantendo apenas valores de 0,7 ou mais
  • O threshold de similaridade por bloco foi definido por ajuste manual
    • Bloco 0: 0,95
    • Bloco 1: 0,94
    • Bloco 2: 0,85
    • Bloco 3: 0,76
    • Bloco 4: 0,81
    • Bloco 5: 0,89
  • Também foram usados pesos manuais ao combinar as distribuições de frequência por bloco
    • Os pesos utilizados foram [0.01, 0.01, 0.1, 1.5, 6, 0.01]
    • Também foi tentada uma busca de pesos via deep learning, mas o resultado não superou o ajuste manual

Avaliação com 20.000 prompts

  • Para os 20.000 prompts completos, foi calculada a distância de Hellinger entre a distribuição aproximada e a distribuição de saída do modelo
    • Média: 0,1677
    • Desvio padrão: 0,1215
    • Mínimo: 0,0013
    • Máximo: 0,9994
  • A distância de Hellinger mede a sobreposição entre distribuições de probabilidade: 0 significa idênticas, 1 significa sem sobreposição
  • Para comparar a qualidade da aproximação, a mesma arquitetura foi treinada mais 3 vezes com random seeds diferentes
    • Seed do modelo original: 1337, training loss estimada 0,9334, validation loss 1,5063
    • Alternate 1 seed: 1442, training loss 0,9293, validation loss 1,5038
    • Alternate 2 seed: 88, training loss 0,9294, validation loss 1,4991
    • Alternate 3 seed: 99999, training loss 0,9339, validation loss 1,4941
  • A distância de Hellinger média entre o modelo original e os modelos alternativos foi de cerca de 0,11 ± 0,08
    • Original vs Alternate 1: 0,1064 ± 0,0823
    • Original vs Alternate 2: 0,1057 ± 0,0817
    • Original vs Alternate 3: 0,1053 ± 0,0828
  • A distância média de 0,17 entre a aproximação e o modelo é maior que os 0,11 entre modelos alternativos, mas está dentro da faixa do desvio padrão, o que sustenta a avaliação de que a aproximação é bastante boa

Interpretando o funcionamento interno do Transformer

  • O modelo pode ser visto, após os embeddings, como várias etapas de transformação no espaço
    • Os 6 blocos do Transformer transformam embeddings de entrada em embeddings de saída dentro de um espaço de embeddings de 384 dimensões
    • O LayerNorm final e a camada linear convertem esse espaço de embeddings em um espaço de logits de 65 dimensões, e o softmax gera as probabilidades do próximo token
  • As transformações internas de cada bloco podem ser vistas como soma de vetores
    • A saída do bloco é a soma da entrada x, da saída da self-attention sa_out e da saída da feed-forward ffwd_out
    • Na análise, observou-se um padrão em que os vetores de saída da feed-forward geralmente têm norma maior que os vetores de saída da self-attention, e em que as duas saídas costumam apontar em direções semelhantes em cada bloco
  • Mesmo somando apenas as saídas da feed-forward, o vetor final de saída costuma apontar em direção parecida com a saída final completa
    • A saída usando só feed-forward tem norma menor, mas direção próxima da saída original
    • Por causa do LayerNorm final, a diferença de norma não é tão importante na entrada da camada linear final
  • Isso não significa que o cálculo de self-attention possa ser removido
    • A feed-forward network recebe a saída da self-attention em sua entrada, como em ffwd_out = self.ffwd(self.ln2(x + sa_out)); portanto, remover a self-attention mudaria a própria saída da feed-forward

Hipótese do subespaço de tokens

  • Certos embeddings podem fazer o modelo prever um token específico com probabilidade quase certa
    • Por exemplo, é possível aprender um embedding que, ao ser passado pelo LayerNorm e pela camada linear após o bloco final, faça a probabilidade do token a ficar quase em 1
    • Os pesos do Transformer ficam fixos e apenas o tensor de embedding é otimizado
  • Cada token não corresponde a um único embedding, mas a vários embeddings
    • Isso é interpretado como um subespaço de embeddings complexo e não linear correspondente a cada token
    • Foi possível aprender facilmente milhares de embeddings únicos para cada token
  • Embora não tenha sido possível determinar matematicamente esse subespaço com precisão, na etapa final uma aproximação linear funcionou bem
    • Os embeddings aprendidos para cada token foram empilhados e submetidos a SVD
    • Um único primeiro right singular vector funcionou bem como aproximação linear unidimensional do subespaço daquele token
  • Ao combinar linearmente vetores de aproximação de subespaços de vários tokens, é possível criar embeddings que distribuem probabilidade entre vários tokens
    • Somar os vetores aproximados de a e b produz uma distribuição que reparte probabilidade entre os dois tokens
    • Porém, por causa do erro de aproximação, da não ortogonalidade entre os vetores de subespaço e de diferenças na magnitude dos logits por token, o resultado não é exatamente uma distribuição 50:50

Correspondência entre saídas feed-forward e subespaços de tokens

  • A saída feed-forward pode ser interpretada como um ajuste da saída do bloco em direção ao subespaço de certos tokens
    • Esses tokens coincidem com os tokens previstos pelo procedimento de aproximação, isto é, os que aparecem após strings de treino que produzem saídas feed-forward semelhantes
  • No exemplo med me Aut, olhando apenas o bloco final, a aproximação prevê o como token seguinte mais provável, seguido de h
    • Ao projetar o mesmo vetor de saída feed-forward na aproximação de subespaços de tokens, os tokens mais semelhantes também foram o, h, i, u, y, nessa ordem
  • No exemplo if and thy, aparece uma correspondência semelhante
    • A aproximação aponta espaço, s e newline como principais tokens previstos
    • Os subespaços mais alinhados com a saída feed-forward também ficaram próximos de espaço, s e newline nessa ordem
  • Em todos os 20.000 prompts, foi definido um critério arbitrário para agregação
    • O critério foi: “os subespaços dos tokens que respondem por 90% da massa de probabilidade na previsão aproximada aparecem na metade superior do ranking de similaridade de cosseno com a saída feed-forward?”
    • No bloco final, 16.357 casos, ou 81,78%, satisfizeram esse critério

Resultados agregados por bloco e comparação com acaso

  • Ao usar as aproximações de subespaço aprendidas em cada posição de bloco, a taxa de atendimento do critério cresce nos blocos mais tardios
    • Bloco 6: 16.357 casos, 81,78%
    • Bloco 5: 10.142 casos, 50,71%
    • Bloco 4: 7.760 casos, 38,80%
  • Aplicar a aproximação de subespaço da etapa final a todos os blocos produz resultados ainda melhores
    • Bloco 6: 81,78%
    • Bloco 5: 68,26%
    • Bloco 4: 58,15%
    • Bloco 3: 57,34%
    • Bloco 2: 52,02%
    • Bloco 1: 49,71%
  • Também foi simulada a probabilidade de o critério ser satisfeito por acaso, com similaridades de cosseno aleatórias
    • Bloco 6: 20,76% ± 0,25%
    • Bloco 5: 20,55% ± 0,26%
    • Bloco 4: 18,37% ± 0,24%
    • Bloco 3: 18,20% ± 0,24%
    • Bloco 2: 17,04% ± 0,23%
    • Bloco 1: 16,31% ± 0,23%
  • Os resultados ficam muito acima do acaso, mas, por causa do ruído de medição e das limitações da aproximação de subespaço, é difícil tratar isso como evidência conclusiva

O papel da self-attention

  • Nessa interpretação, boas previsões dependem de mapear o prompt para a classe correta de strings do corpus de treino
  • O eixo responsável por esse mapeamento é a self-attention
    • A camada de attention identifica padrões entre os tokens do prompt
    • Esses padrões podem ser algo simples, como o padrão final y l, ou tipos de token mais gerais, como vogais ou letras maiúsculas em posições específicas
  • Os pesos aprendidos dos attention heads determinam a quais padrões eles respondem
    • Quando a saída do attention head passa pela feed-forward network, ela se torna uma representação no espaço de embeddings que carrega informação sobre a distribuição dos tokens que apareceram após strings de treino semelhantes
  • O modelo analisado tem 6 blocos e 6 attention heads por bloco, então um mesmo prompt pode ser avaliado em relação a vários padrões latentes

Conclusão e limitações

  • A evidência de que o procedimento de aproximação e a saída real do Transformer são semelhantes é forte
  • A evidência de que o procedimento de aproximação corresponde ao cálculo interno real do Transformer é menos clara, mas sugere que a ideia pode estar parcialmente correta
  • Não está claro se os resultados obtidos em um único Transformer pequeno se generalizam para modelos maiores ou outros datasets
  • Este projeto é um processo de fazer perguntas ao interior do modelo e desenhar experimentos, e ajuda mais a enxergar a complexidade dos modelos de linguagem do que a reduzir sua “mágica” a um mecanismo simples

1 comentários

 
GN⁺ 2024-02-05
Opiniões do Hacker News
  • Alguns dos temas do post pai não deveriam ser uma grande surpresa para quem leu https://people.math.harvard.edu/~ctm/home/text/others/shanno...
    Se você não lê a literatura fundamental da sua área, até fenômenos que surgem como consequência natural de trabalhos já estabelecidos acabam parecendo mistérios inexplicáveis
    Ainda assim, os experimentos parecem bastante completos à primeira vista, e valorizo muito a quantidade de trabalho detalhado envolvida
    Há um trade-off difícil entre aprender a teoria existente e tentar derivar tudo de novo do zero. Sem uma base tradicional, novas descobertas são possíveis; com essa base, dá para entender certos fenômenos mais profundamente
    Nos comentários aqui, vejo várias pessoas surpresas que um modelo que maximiza a log-verossimilhança de uma sequência, dados os dados, não se afaste magicamente desse comportamento durante a inferência. Trata-se de um modelo de estimação de densidade; esperavam que ele recitasse Shakespeare do nada?
    Voltar ao básico faz com que esses experimentos sejam entendidos muito melhor. Já existe uma base matemática muito clara que explica isso, bem como os chamados fenômenos emergentes
    Mais especificamente, há várias camadas, e o tratamento de Shannon para sistemas ergódicos é um bom ponto de partida. Há alguns desalinhamentos aqui, mas parece uma correspondência próxima o suficiente para entender a dinâmica geral

    • Pesquisadores muito inteligentes de teoria da informação vêm olhando para redes neurais do ponto de vista da teoria da informação há alguns anos e até publicaram artigos famosos, mas isso não explicou grande parte das redes neurais. Ainda assim, foi interessante
      Não é raro pessoas inteligentes dizerem: “esta estrutura matemática se parece com aquela ideia; se adicionarmos ou removermos algumas estruturas, tudo fica explicado”. Mas, na prática, há muita coisa que não sabemos
      Nunca vi teóricos desta área chegarem com uma teoria e, a partir dela, criarem algo novo ou fazerem previsões úteis. Normalmente, tenta-se uma coisa aqui e outra ali; se funcionar, depois se acrescenta uma explicação plausível para por que funcionou; se não funcionar, fica esquecido
      Também houve recentemente um texto vendo transformers como suavização por kernel: https://arxiv.org/abs/1908.11775
    • Entendo o que você quer dizer, mas o fato de haver convergência por caminhos alternativos de diferentes profundidades também é um sinal
      Redescobertas repetidas não são necessariamente desperdício; podem ser um processo de confirmar e validar uma verdade profunda à qual se chega por várias rotas
    • Em outra thread vizinha, estão discutindo o que significa para direitos autorais o fato de redes neurais se ajustarem aos dados de treinamento dentro de uma certa margem de erro
      Muitos livros-texto de teoria da informação já apontam a natureza endereçável por conteúdo (content-addressable) dessas redes[1], e elas também são usadas em aplicações como compressão por esse motivo[2][3]
      Portanto, não surpreende que, quando o NYT inseriu alguns parágrafos de seus próprios artigos como prompt em um modelo da OpenAI, eles tenham sido reproduzidos quase literalmente
      [1] https://www.inference.org.uk/itprnn/book.pdf
      [2] https://bellard.org/nncp/
      [3] https://pub.towardsai.net/stable-diffusion-based-image-compr...
    • Então fica a pergunta: por que Shannon não criou o GPT?
  • Quando vi a observação de que, se o Google mandar repetir a mesma palavra continuamente, isso pode fazer o ChatGPT regurgitar os dados de treinamento literalmente[0][1], pensei exatamente na mesma ideia. Fico feliz que alguém tenha de fato implementado
    Isso levanta duas perguntas adicionais

    1. Essa abordagem de “IA, pegue a IA” seria mais eficiente em energia do que comprimir os dados de treinamento no modelo via retropropagação por descida de gradiente e depois rodá-lo em um coprocessador de IA dedicado?
    2. Esse resultado poderia servir como prova nos processos em andamento contra a OpenAI e a Stability AI?
      [0] Antes era possível. Agora a OpenAI bloqueia a geração se você preencher a janela de contexto com uma única palavra
      [1] https://arxiv.org/abs/2311.17035
    • Essa abordagem não pode ser mais eficiente do que executar o modelo original. Isso porque é preciso rodar o modelo original para obter as ativações e, em seguida, procurar no corpus strings parecidas com essas ativações para calcular as estatísticas do próximo token
      Não há muitas etapas sendo puladas; pelo contrário, muito trabalho extra é acrescentado
      Acho que, mesmo separando completamente o corpus de treinamento do modelo e o corpus usado para buscar strings com ativações semelhantes, o resultado seria quase o mesmo. A parte difícil, afinal, é fazer com que strings com estatísticas de próximo token parecidas produzam ativações parecidas
      Nos pesos por camada [0.01, 0.01, 0.1, 1.5, 6, 0.01], o mais importante é a penúltima camada, e nesse ponto a entrada já foi bastante transformada. Portanto, não se deve esperar substituir o transformer por um simples grep sobre os dados de treinamento com base nisso
      O motivo de o peso da penúltima camada ser muito maior que o da camada final provavelmente são as cabeças de indução. Como em https://transformer-circuits.pub/2021/framework/index.html, pode ser uma estrutura que implementa a função de copiar strings repetidas da entrada, em que a penúltima camada decide o que procurar e a camada final realiza a cópia
    • Se a ideia é dizer que a saída de um LLM segue probabilidades do próximo token baseadas nos dados de treinamento, isso é um fato básico bem conhecido, então me parece improvável que esse resultado sirva como prova
      A contribuição deste texto, como o próprio autor diz, é mostrar isso a leitores técnicos que estão criando seu próprio GPT, ao contrário de textos focados no próprio transformer e em “como isso é implementado?”
    • Pela minha experiência antes de isso ser bloqueado, ele alucinava coisas que pareciam dados de treinamento reais
      Olhando de perto, eram READMEs do GitHub inexistentes e incoerentes, folhetos informativos sem conteúdo, conversas aleatórias e coisas do tipo
    • Achei interessante que o artigo do arXiv linkado trate isso como ataque, ética e divulgação responsável
      Mas raspar a internet inteira para treinar esses modelos nunca é chamado de ataque
    • Se uma obra tem copyright, então obviamente a pessoa também tem direitos sobre um arquivo zip dessa obra
      Então por que não deveríamos considerar que ela também tem direitos sobre a distribuição de probabilidade dos caracteres dentro dessa obra?
  • Assim que conheci o NanoGPT do Andrej Karpathy, treinei-o com a versão russa de War and Peace, e achei interessante que, mesmo sendo um modelo de apenas 3 MB, ele captou quase toda a gramática russa
    O russo tem uma estrutura sintética e flexional complexa. Por exemplo, depois da preposição “na” (“upon”), deve vir um substantivo no acusativo, o que aparece como -a para substantivos masculinos animados, ausência de terminação para substantivos inanimados, -ia para substantivos terminados em “consoante suave”, -u para substantivos femininos, e assim por diante
    Além disso, o verbo “usar” exige o caso instrumental quando vem seguido de um substantivo usado como ferramenta
    Não era perfeito e havia erros, mas foi interessante que o NanoGPT inferiu regras específicas e complexas com apenas 3 minutos de treinamento. Procurei no texto original as frases exatas que ele gerou, mas não encontrei nenhuma correspondência literal
    Ainda assim, embora entendesse a gramática em certa medida, semanticamente era um completo absurdo

    • Flexões de terminação devem estar entre os tokens mais comuns no texto de treinamento, então não é tão surpreendente
  • Havia uma boa visualização 3D mostrando o mesmo sistema, e acho que funcionaria bem ler junto
    LLM Visualization (https://bbycroft.net/llm)
    https://news.ycombinator.com/item?id=38505211

    • Agradeço o esforço colocado nessa visualização, mas, do ponto de vista de quem trabalha com redes neurais há 9 anos, ela me confundiu muito mais do que ajudou
      Acho que foi por tentar mostrar tudo de uma vez e por não empurrar as coisas para conceitos abstratos, mas não tenho certeza
  • É um bom projeto, mas o modelo analisado é, na prática, um modelo de brinquedo, tanto em tamanho quanto na quantidade de dados de treinamento
    Por isso, esse modelo talvez possa ser aproximado por um modelo mais simples, provavelmente um modelo de linguagem n-gram, mas é difícil vê-lo como representativo de como modelos de linguagem maiores funcionam

    • Provavelmente é verdade. Se criarmos um modelo menor, acho que também poderemos criar uma explicação muito mais simples de como ele funciona
  • Fico me perguntando se o autor está afirmando que LLMs são geradores de texto de cadeia de Markov.
    Ou seja, isso quer dizer que a distribuição de probabilidade do próximo token gerado é igual à probabilidade daquela sequência de tokens nos dados de treinamento?
    Se for assim, será que criar “simplesmente” uma cadeia de Markov a partir dos dados de treinamento originais daria um desempenho parecido com o de um LLM?

    • Um LLM é uma cadeia de Markov no seguinte sentido:
      o estado é um vetor de tokens do tamanho do comprimento de contexto, e o modelo descreve a matriz de transição. Para um dado vetor de tokens do tamanho do comprimento de contexto, ele produz as probabilidades do próximo vetor de tokens com o mesmo tamanho de contexto.
    • Não. Um LLM não simplesmente copia o mesmo texto; ele está mais próximo de “classificar” o texto com self-attention e depois aplicar uma cadeia de Markov simples.
      A parte difícil é a classificação: saber quais textos dos dados de treinamento são “parecidos” com o texto do prompt.
      Usando o exemplo do post do blog, fica assim:
      Original string: 'And only l'
      Similar strings: 'hat only l' 's sickly l' ' as\nthey l' 'r kingly l'
    • O texto diz que “implementou em código imperativo aquilo que ele propõe que o transformer faz, e produz uma saída muito parecida com a do transformer”.
      Isso sugere que pode haver uma forma de contornar o transformer e obter o mesmo resultado. Fico curioso se seria mais eficiente.
      Por exemplo, dado um modelo-base, talvez fosse possível treinar outra coisa e executá-la em um dispositivo muito menor.
  • É bem difícil entender exatamente o que o autor afirma ter demonstrado.
    Li algumas vezes a seção “Interpretation: Why Does the Approximation Work?”, mas ela me parece uma explicação mecânica das etapas do transformer. Não sei qual é a tese central.

  • Fico me perguntando se já é conhecido o fenômeno de o deslocamento de attention e FF apontarem, em geral, na mesma direção.
    Já acho um pouco surpreendente que eles estejam no mesmo espaço latente ao longo das camadas. Uma rede FF não poderia fazer uma rotação arbitrária? Acho que entendi algo errado.

    • É uma representação em 2D de vetores de dimensão muito alta.
      Inevitavelmente algo fica de fora, e uma das coisas que fica de fora é descrever com precisão uma rotação arbitrária em um espaço de alta dimensão.
    • Seria bom verificar trocando a soma da attention por um escalonamento