Por que os transformers mudaram tudo: Input embeddings

Para entender como um Transformer funciona, vamos dissecar o diagrama abaixo que mostra todas as etapas do processo.

Mechanism of the Transformer. (https://www.researchgate.net/figure/1-Mechanism-of-the-Transformer_fig2_340659282)

Input embeddings

Quando falamos no uso de redes neurais e mais amplamente técnicas de aprendizado de máquina trabalhamos exclusivamente com números. O que quero dizer com isso é que todo tipo de dado não estruturado, ou até mesmo estruturado, mas que esteja na forma de texto, por exemplo, tem que primeiro ser transformado em números para que possa ser “aprendido” pelos modelos de aprendizado de máquina.

Daí surge a questão: como transformar texto em números?

Bag of words

Existem algumas formas de fazer isso, e foram-se descobrindo novas alternativas para tentar resolver esse problema. Uma das primeiras soluções encontradas foi de dar uma “posição” a cada palavra e contar o número de aparições de cada uma das palavras no texto de entrada, a técnica chamada de bag of words . A imagem abaixo demonstra como isso é feito: cada palavra tem uma posição na tabela abaixo (cada palavra do vocabulário é uma coluna) e o valor de cada célula é a contagem de quantas vezes cada palavra apareceu no texto de entrada, representado pelas linhas.

BOW on Surfin’ Bird (https://openclassrooms.com/en/courses/6532301-introduction-to-natural-language-processing/8081284-apply-a-simple-bag-of-words-approach)

Uma característica dessa representação textual é que a matriz resultante é extremamente esparsa (palavra muito usada no contexto de aprendizado de máquina, nesse caso indica que existem muitos “0” na matriz). Pode-se imaginar que para contemplar a maior parte do vocabulário de uma língua, para que o modelo seja satisfatório, seria necessário muito mais colunas do que as representadas, sendo uma matriz de muitos “0” e apenas algumas colunas preenchidas com algum número. Como é de se imaginar, o “desperdício” computacional aqui é enorme, o português é uma língua com aproximadamente 370 mil palavras, imagine ter uma matriz de 370 mil (!) colunas para realizar os cálculos, e a maioria disso serem multiplicações por 0. Outra informação que é perdida aqui é a posição das palavras, algo que é extremamente importante para a interpretação de um texto. Pense nas seguintes frases: “Orlando é pai de Leandro” é completamente diferente de “Leandro é pai de Orlando” e as frases seriam representadas da mesma forma.

Daí pode surgir uma outra dúvida: por que não associar um identificador a cada palavra e ter uma matriz em que os valores são o índice (ou identificador de cada palavra) e as colunas serem a posição de cada palavra no texto, por exemplo? Pela natureza de redes neurais (e da maioria das técnicas de aprendizado de máquina) as features, no exemplo acima as colunas, são sempre variáveis quantitativas. Em outras palavras, se eu digo que a palavra “eu” tem o identificador 1 e a palavra “você” tem o identificador 2 isso não significa que “você” é maior que “eu”, pois apesar de 1 < 2, esses números são apenas identificadores, porém, um modelo “entenderia” que isso seria uma grandeza quantitativa, gerando uma confusão enorme e sendo impossível chegar em um resultado satisfatório para aplicações reais.

Palavras como vetores

Para resolver os problemas citados acima, algumas alternativas foram propostas com uma ideia principal em mente: palavras que aparecem juntas têm significados semelhantes e contextos semelhantes. A ideia aqui é que palavras podem ser representadas de acordo com n características arbitrárias. Para ilustrar essa ideia, temos o seguinte exemplo: a palavra “deságio” e a palavra “juros” são ambas palavras bastante utilizadas em contextos de textos financeiros, portanto, poderíamos definir uma grandeza quantitativa que seria o “financeirês”, ou seja, o quanto uma palavra é utilizada no contexto de textos financeiros, e teria seu domínio entre 0 e 1, quanto mais próximo de 1, mais essa palavra é utilizada nesses contextos. As duas palavras citadas certamente estariam próximas de 1, o que indica que se criássemos n grandezas, poderíamos classificar todas as palavras do vocabulário e palavras semelhantes teriam valores semelhantes para essas grandezas.

Nessa linha de pensamento surgem então algorítmos como o GloVe: Global Vectors for Word Representation, que representam palavras em n dimensões no espaço. Essas dimensões não tem nenhum significado que possa ser interpretado facilmente por nós seres humanos, como o “financeirês”, mas para os modelos esses significados existem.

Essa então é a primeira etapa de um Transformer e de grande importância! Nos vemos na próxima etapa, a de positional encoding.

Prevendo gols em partidas de futebol com um modelo de IA

Neste artigo, apresentarei os resultados e parte do processo de desenvolvimento do nosso atual modelo de probabilidade de gols.

Definindo o problema

Em primeiro lugar, explicarei nossa abordagem ao problema de prever gols em uma partida. Ao construir um modelo de previsão, deve-se primeiro decidir se ele fará previsões durante o jogo ou apenas antes de acontecer. É claro que o modelo que prevê partidas ao vivo também será capaz de fazer previsões antes dela começar (t=0), e por isso, optamos por desenvolver esse tipo de modelo.

Tecnicamente falando, nosso modelo teria inúmeras variáveis independentes, chamadas de recursos em modelos de IA, como entradas e, além disso, o número relativo de gols que gostaríamos de obter a probabilidade. Um exemplo é mostrado abaixo.

Visão geral do modelo: Gh = gols da casa, Ga=gols do visitante,
Ch=escanteios da casa, Ca=escanteios do visitante

Preparando os dados

Para resolver o problema, optamos por usar modelos de aprendizado de máquina porque as relações entre as variáveis independentes e a variável de destino são desconhecidas e muitas vezes muito complexas. Além disso, não estamos interessados em tirar conclusões do próprio modelo de dados (ou seja: como é a relação entre variáveis independentes e alvo), mas estamos apenas interessados em obter a saída (previsão).

Conforme discutido no ótimo artigo The Wisdom of the Crowd, a melhor forma de estimar as probabilidades de um determinado evento no futebol seria pedir a vários apostadores que estimassem as próprias probabilidades. Felizmente, isso se traduz nas probabilidades de um determinado mercado para uma casa de apostas ou uma bolsa de apostas. Como você deve saber, a probabilidade está relacionada à probabilidade implícita da casa de apostas pela seguinte equação:

odd = 1/probbm

A probabilidade implícita da casa de apostas está relacionada com a probabilidade justa pela seguinte equação:

probfair + M = probbm

onde M é a margem da casa de apostas.

Resolvendo esta equação podemos chegar a uma aproximação da probabilidade justa calculada pela casa de apostas. Esse resultado é o alvo do nosso modelo e foi usado para treiná-lo.

Treinando o modelo

O modelo foi treinado usando muitas estatísticas da partida, tanto estatísticas ao vivo quanto estatísticas pré, e teve a probabilidade justa de o jogo terminar com a quantidade especificada de gols totais como resultado. O conjunto de dados foi dividido em dois conjuntos, o conjunto de treinamento e o conjunto de teste, como é usual em modelos de aprendizado de máquina. O ponto principal aqui foi que essa divisão garantiu que as partidas do conjunto de teste não fossem encontradas no conjunto de treinamento, pois isso seria considerado vazamento de dados.

Além disso, o modelo foi ajustado para usar os melhores hiperparâmetros usando grid search, sempre classificando os resultados pela função de perda, neste caso, a função de erro quadrático médio. Abaixo estão os resultados da melhor configuração de modelo no conjunto de teste.

MétricaValor
Erro Absoluto Médio0.024
Erro Quadrático Médio0.00096
Erro médio-0.0021
Desvio-padão do erro0.031
Métricas do conjunto de teste
Valor de perda no treinamento (azul) e no teste (laranja)

Podemos ver acima que a perda de treino diminui muito no início do treino e a perda de teste é bem pequena já no início. À primeira vista, esse gráfico não parecia certo. (1) Este parece ser um exemplo de um modelo que tem uma taxa de aprendizado muito alta: o modelo se ajusta muito aos dados na primeira epoch e depois disso a taxa de aprendizado é tão alta que o modelo não consegue aprender mais nada . (2) Também é estranho, a princípio, que a perda de teste seja muito menor do que a perda de treinamento na primeira epoch.

Em relação a (1), alguns fatores devem ser considerados primeiro: 1. o conjunto de treinamento é muito grande e 2. a perda de teste é calculada após a primeira epoch de treinamento. Se o modelo aprendeu tudo o que pode com os dados da primeira epoch, espera-se que a perda de validação seja muito menor do que a perda de treinamento (lembre-se de que a perda é calculada no final de cada processamento de lote (batch), mesmo quando o modelo é muito “burro”).

Isso levanta a questão: Por que usar essa configuração então? Por que não usar uma taxa de aprendizado mais baixa? A resposta é: porque depois de testar exaustivamente o modelo com várias configurações, aquele que melhor desempenhou teve a configuração escolhida, embora a maioria não tenha tido desempenho muito diferente.

Já em relação a (2), o fato de a perda de teste ser menor que a perda de treinamento em épocas posteriores se deve ao fato de que a regularização foi utilizada no treinamento do modelo. Como você deve saber, a regularização é usada no treinamento, mas não na previsão. Isso é bem explicado por essa thread.

Histograma do conjunto de teste

A distribuição dos erros pode ser claramente vista como normal, como seria de esperar, com a média e o desvio padrão descritos acima. Com esta informação, podemos concluir que aproximadamente 95% dos erros cairão entre -5,2pp e 4,4pp de diferença da verdadeira probabilidade. Este é um resultado muito interessante, especialmente considerando que as casas de apostas e as bolsas de apostas têm em média 2,5pp de diferença entre as suas probabilidades implícitas e podem mesmo apresentar diferenças muito superiores.

Por que os transformers mudaram tudo: LSTMs

Depois de ver os problemas dos RNNs simples vistos antes, pesquisadores propuseram uma nova abordagem para modelos relacionados à memória. A nova abordagem foi chamada de modelos LSTM.

Arquitetura Long Short Term Memory (LSTM)

Embora os modelos LSTM sejam uma variante das Redes Neurais Recorrentes, eles apresentam mudanças muito importantes. A ideia é separar o estado da célula dos valores de entrada atuais.

Source: https://colah.github.io/posts/2015-08-Understanding-LSTMs/

A linha horizontal superior é chamada de estado da célula, é considerada a “memória” de longo prazo da célula. Outro componente especial da arquitetura são os portões. Sempre que você vir um sinal de multiplicação, significa que é um portão.

Source: https://colah.github.io/posts/2015-08-Understanding-LSTMs/

Portões (gates)

Nesse caso, eles são precedidos por uma função de ativação sigmóide e a explicação para isso é que a saída da função sigmóide vai de 0 a 1, portanto, a operação de multiplicação funciona como um portão para deixar alguma informação passar para o próximo estágio ou ser esquecida . Dessa forma, o estado da célula pode ser alterado e apenas as coisas importantes no momento são lembradas pelo modelo.

A saída

A saída do modelo é o valor h, que pode ser visto como uma versão filtrada do estado da célula. Ele é filtrado no último portão à direita, que tem como entrada o estado da célula passado por uma função tanh.

Outras variantes

conexões de olho mágico

Introduzida por Gers & Schmidhuber (2000), a abordagem olho mágico fornece aos portões o próprio estado da célula como uma entrada.

Source: https://d3i71xaburhd42.cloudfront.net/545a4e23bf00ddbc1d3325324b4c61f57cf45081/2-Figure1-1.png
Gated Recurrent unit (GRU)

Introduzido por Cho, et al. (2014), ele mescla os portões de entrada e de esquecimento em um único portão, chamado de “portão de atualização”. Isso atinge uma arquitetura mais simples, sendo mais fácil de treinar e computacionalmente mais barata. Esta variante ganhou muita popularidade ao longo dos anos.

Source: https://colah.github.io/posts/2015-08-Understanding-LSTMs/

Conclusão

A introdução da arquitetura LSTM trouxe muito mais possibilidades para resolver problemas. O modelo proposto e suas variantes têm sido utilizados com sucesso em várias soluções relacionadas à memória.

O próximo grande passo que será discutido no próximo artigo é a arquitetura Attention introduzida pelo Google. Uma abordagem que, novamente, mudou tudo no mundo do aprendizado de máquina e criou muito mais possibilidades.

Por que os transformers mudaram tudo: antes

Nesta série de artigos, discutirei por que a invenção dos transformers, publicada pela equipe de IA do Google no artigo intitulado Attention Is All You Need (2017), foi tão importante para o aprendizado de máquina e os campos de aprendizado profundo.

Antes de tudo, para entender as realizações dessas ideias propostas, é preciso primeiro entender como as coisas eram feitas antes. Todos nós sabemos que o contexto importa, em alguns casos, importa muito. Por exemplo, ao ler este artigo, você mantém todo o texto anterior em mente para compreender as próximas frases, caso contrário, não faria sentido para você.

Claro, não demora muito para se chegar à conclusão de que pode haver muitas preocupações ao lidar com o contexto. Continuando com a leitura como exemplo, quanto tempo dura o seu contexto? Em outras palavras, de quantas frases (ou ideias) pra trás você se lembra quando lê um texto? Obviamente a resposta é que isso varia. Então, como podemos inserir isso em um modelo?

Usar uma rede neural com palavras em suas respectivas posições como entrada não funcionaria, pois o modelo teria que ter um tamanho de entrada fixo e estabelecemos que um contexto não pode ter um comprimento fixo. Uma das primeiras ideias foi usar RNNs ou Redes Neurais Recorrentes (Recurrent Neural Networks).

Redes Neurais Recorrentes (Recurrent Neural Networks)

As redes neurais recorrentes são assim:

Source: wikipedia.com

O h na imagem é simplesmente uma função de ativação e pesos. x são as entradas em cada estado e o é a saída de previsão para esse estado. O importante a notar aqui é que os pesos são iguais entre todas as unidades da rede. Isso confere as seguintes vantagens ao modelo:

  1. Informações sobre estados passados são usadas pela rede para calcular a saída (previsão) do modelo;
  2. A quantidade de estados passados pode variar indefinidamente;
  3. O modelo é muito eficiente computacionalmente e, portanto, são facilmente treinados.

Infelizmente, embora as vantagens desse tipo de modelo pareçam grandes, existem alguns problemas que dificultam essa abordagem:

  1. As informações de vários estados anteriores são quase totalmente perdidas e ficam praticamente indisponíveis na última etapa do cálculo (a última camada);
  2. Este modelo sofre do problema de gradiente Desaparecendo/Explodindo (Vanishing/Exploding).

Essas limitações restringem os casos de uso desse método a situações em que o número de estados passados a serem considerados é relativamente pequeno. No entanto, a arquitetura proposta pode ser alterada para corrigir esses problemas.

Problema de gradiente Desaparecendo/Explodindo (Vanishing/Exploding)

O problema do gradiente de desaparecendo/explodindo ocorre porque as entradas dos estados anteriores são multiplicadas pelos pesos e passam pela função de ativação muitas vezes antes de atingir a camada final.

Source: https://dustinstansbury.github.io/theclevermachine/derivation-common-neural-network-activation-functions

Se dermos uma olhada nas funções de ativação e suas derivadas na imagem acima veremos que nos limites positivo e negativo as derivadas vão a zero, exceto a função linear (que ninguém usa, pois não introduz não linearidade ao modelo). Dessa forma, você pode imaginar que uma entrada sendo multiplicada muitas vezes por um único valor e passada muitas vezes por uma função de ativação teria uma pequena derivada final.

Como a atualização dos pesos depende da derivada parcial da entrada, se for muito pequena, as atualizações ficam muito pequenas também e isso prejudica o desempenho do modelo. O gradiente explosivo tem mais a ver com a forma como os pesos são inicializados, eles podem crescer muito e as atualizações podem se tornar tão grandes que o modelo nunca converge.

Algumas soluções podem ser aplicadas para resolver o problema apresentado:

  1. Use outras funções de ativação, como ReLU. Esta função de ativação tem uma derivada constante, portanto, o gradiente não desaparece (para uma entrada positiva);
  2. Gradient clipping: limite o gradiente para um valor máximo;
  3. Use Batch Normalization: esta técnica aprende um processo de normalização para transformar a saída de cada camada.

Você também pode tentar alterar a arquitetura do modelo para superar esse problema.

Para o próximo artigo, discutirei a próxima arquitetura de Modelos de Linguagem Natural, que surgiu como uma alternativa para resolver os problemas impostos pelas RNNs.

O que é Machine Learning?

Machine learning ou aprendizado de máquina é um ramo da Inteligência Artificial e, como tal, é um campo da ciência que tenta criar modelos para tomar decisões melhores ou mais rápidas. Especificamente, os algoritmos de aprendizado de máquina fazem isso analisando dados e adaptando-se a eles. À medida que o modelo se adapta, ele se torna, idealmente, mais preciso nas suas previsões.

Ok, mas como isso é útil?

Os casos mais úteis de aprendizado de máquina são casos em que é muito difícil formular um algoritmo para resolver o problema. Um exemplo é a classificação de objetos em imagens. Imagine tentar criar um algoritmo para classificar os grãos de café como podres ou saudáveis, a cor é importante, mas também é como os grãos podem estar orientados na foto, a que distância eles estão da câmera (pois isso afetaria seu tamanho na imagem) e muitos outros fatores. Isso pode se tornar uma tarefa quase impossível.

Mas como nós, humanos, podemos fazer isso sem nem pensar? Temos todas essas regras e padrões em nossas mentes que foram aprendidos. E é exatamente isso que os modelos de aprendizado de máquina farão. Essencialmente, todos os aspectos relevantes dos dados (chamados de features), serão utilizados pelo modelo de forma que ele tente “entender” o efeito deles no resultado esperado.

Como você pode imaginar, a primeira coisa a descobrir é a pergunta que está sendo feita. Isso é um problema de previsão ou classificação? Em que estou interessado? Estou interessado em saber um número, por exemplo, a temperatura para amanhã na minha cidade ou quero saber se os grãos de café em uma imagem estão estragados ou bons para consumo?

Uma vez formulada a questão, deve haver a garantia de que os dados em mãos são confiáveis, limpos e suficientes para a precisão necessária do modelo. À medida que a complexidade do problema aumenta, também aumenta o tamanho da amostra de treinamento necessário. Algoritmos também não são inteligentes como humanos (ainda), então eles geralmente precisam de MUITO dado para entender e generalizar bem. Muito mais que um humano precisaria.

Os métodos de aprendizagem

Os algoritmos de aprendizado de máquina têm 4 tipos de métodos de aprendizado: aprendizado supervisionado (supervised), não supervisionado (unsupervised), semissupervisionado (semi-supervised) e por reforço (reinforcement learning).

Aprendizado supervisionado (supervised)

O modelo usa dados rotulados para aprender. O que isso significa é que: algo (geralmente um ser humano) tem que dizer ao modelo o que é o quê nos exemplos da amostra de treinamento/teste. Este método de aprendizado geralmente oferece melhor precisão do que o método não supervisionado, mas como ele tem o requisito de que os dados precisam ser rotulados, o processo de coleta de dados pode ser inviável. Os alvos deste tipo de modelos são classes ou valores numéricos. Os casos de uso são muito amplos e os modelos podem ser usados teoricamente para qualquer coisa que seja dividida em classes ou seja medida como um número.

Aprendizado não supervisionado (unsupervised)

Nesse método de aprendizado, o modelo aprende com dados não rotulados, o que significa que ele tenta identificar semelhanças com os dados fornecidos. Embora esse método de aprendizado produza resultados menos confiáveis do que os supervisionados, eles aproveitam o fato de que dados não rotulados são muito mais fáceis de obter. Os alvos desses modelos são associações (entre pontos de dados) ou clusters (grupos). Exemplos de casos de uso são perfis de clientes de marketing (agrupamento) para comportamento semelhante e itens de compra relacionados (associação).

Aprendizado semissupervisionado (semi-supervised)

Este método é uma mistura dos dois, parte dos dados é rotulada, mas a maioria não é. Pode ser útil se houver muitos dados não rotulados e dados rotulados suficientes para tirar algumas conclusões sobre os dados não rotulados. Os dados rotulados são usados para ajustar um modelo para prever os rótulos dos dados não rotulados. Então, um modelo supervisionado pode aprender usando os dados totais.

Aprendizado por reforço (reinforcement)

O aprendizado por reforço usa uma abordagem diferente para aprender padrões: um agente realiza diferentes ações em um ambiente e depois avalia se o resultado dessas ações foi bom ou ruim, desta forma, reforça os bons padrões e penaliza os padrões ruins. Situações em que não se pode determinar se uma ação é boa ou ruim são casos de uso desse método. O melhor e mais comum exemplo é um jogo, porque as regras são claras, o objetivo do resultado é claro, mas a qualidade das jogadas nem sempre é clara. Outros exemplos são caminhar (ou voar) do ponto A ao ponto B.

Espero que tenha gostado desse post, sinta-se convidado a comentar e mandar mensagens!