De maneira bem simplista, quando o ChatGPT escreve uma sentença, o que ele faz, na verdade, é perguntar (várias e várias vezes): "dado o que está escrito até agora, qual seria a próxima palavra aceitável?". "Aceitável" aqui, significa algo que faz sentido ser escrito por alguma entidade que conhece centenas de milhares (milhões? bilhões) de conteúdos similares.
Então, quando o ChatGPT encontra a próxima palavra (ou, mais especificamente, o token) mais aceitável, ele escreve, e pergunta de novo, qual seria a próxima mais aceitável.
Faça um teste e escreva na barra de busca do Google, "Qual o melhor restaurante de ". O autocomplete do Google sugere vários nomes de cidades, que, por sua vez, são as próximas palavras bem aceitáveis que completam a minha frase! Esses nomes de cidades são sugeridos através de probabilidades, que pode envolver consultas similares feitas anteriormente, reviews de sites de restaurantes, ou textos como esse que escrevo.
Como cada cidade tem probabilidade diferente de aparecer na lista (algumas cidades nem aparecem), pensando no ChatGPT, qual deveria ser a próxima cidade que ele escolheria para compor a minha frase?
Uma maneira simples de pensar é que o ChatGPT utilizaria as palavras com maior probabilidade. No entanto, alguns estudos científicos apontam que escolher sempre a palavra com maior probabilidade geraria conteúdo com palavras repetidas, sem criatividade. Utilizar um pouco de aleatoriedade para escolher a próxima palavra aceitável tende a gerar conteúdo "mais interessante". Existe, inclusive, uma variável para isso, chamada "temperatura", que determina a chance de palavras com menor probabilidade serem escolhidas.
Sabendo que eu escolho as próximas palavras de uma lista, e fazendo isso diversas vezes, como que o ChatGPT gera essa lista? Ou melhor, como geramos essas probabilidades das palavras?
Antes de pensar na probabilidade da próxima palavra, vamos dar um passo para trás, e pensar em formar uma palavra baseada nas probabilidade das letras. Vamos usar como base os dados de alguns verbetes da Wikipedia. Usando o verbete sobre Cão, as cinco letras com maior probabilidade de ocorrência, são:
A: 11.75%, E: 11.36%, O: 10.57%, S: 8.37%, I: 6.69%
No verbete sobre Avião, as letras com maior frequência de ocorrência, são:
A: 12.82%, E: 11.69%, O: 10.47%, S: 7.56%, R: 7.01%
E finalmente, na verbete sobre Belém do Pará, as cinco letras com maior frequência de ocorrência, são:
A: 12.37%, E: 10.92%, O: 10.01%, I: 7.34%, R: 7.01%
Como é possível observar nas probabilidades acima, independente da fonte do conteúdo, as letras com maior probabilidade de aparecer são razoavelmente parecidas. Agora, parece fazer mais sentido que escolher sempre as com maior probabilidade não é sempre uma boa ideia, certo?
Se fizéssemos esse exercícios com diversas palavras do Português, observariamos notável semelhança nos resultados. Poderíamos contabilizar todas as verbetes da Wikipedia, ou até todas as palavras do Português. Se fizéssemos isso, teríamos muitos mais dados.
Mas, com todos esses dados, seríamos capazes de chutar com mais confiança qual seria a próxima letra aceitável?
Considerando um grau de aleatoriedade na escolha das lestras, essa seria uma possível saída, dado as probabilidades que descobrimos acima:
aaeiribrdieirbabiseoruashtiarosohaeoshaxiolirs
Se considerarmos que o espaço em branco é uma letra, e utilizarmos a sua probabilidade junto às demais, teríamos algo como:
aa eir ibrd ie irbab ise o ruas hti aros o haeo shax iol irs
Poderíamos melhorar nosso algoritmo para geração de palavras considerando a distribuição dos tamanhos das palavras da nossa fonte de dados, mas isso não nos levaria a gerar palavras reais. Isso acontece pois estamos escolhendo as próximas letras aleatoriamente, quando na verdade, precisamos de algumas regras para isso.
Por exemplo, sabemos por experiência que depois de uma letra "q" vem (quase sempre) uma letra "u". Sabemos também que podemos colocar qualquer consoante depois de uma vogal. Sabemos também que podemos colocar algumas consoantes juntas, como "ss" e "rr".
Ou seja, além da probabilidade das letras ocorrerem individualmente, podemos calcular a ocorrência de pares de letras (ou 2-grams/bigram, como conhecido tecnicamente).
Se calcularmos o bigram mesma verbete são Cão, teríamos o seguinte resultado:
DE: 2.58%, ES: 2.33%, OS: 2.11%, DO: 2.04%, AS: 1.73%
Enquanto no verbete de Avião, teríamos:
DE: 2.7%, ES: 2.29%, ER: 1.96%, AS: 1.59%, DO: 1.58%
Pouco supreendente neste momento, o Bigram de Belém do Pará, é:
DE: 3.33 %, DO: 2.2 %, RA: 1.97 %, CO: 1.68 %, ES: 1.65 %
Ou seja, ao usarmos probabilidade dos bigrams (eventualmente trigrams ou n-grams), temos maior chance de criar próximos tokens (que podem ser entendidos como pedaços de palavras) que fazem sentido.
Assuma agora que o ChatGPT trabalha a nível de tokens, e que existem dezenas de milhares de tokens que são utilizados regularmente em uma língua como Português. Assuma também que o ChatGPT teve acesso a milhares (milhões? bilhões?) de textos em Português e utiliza essa base de dados e suas probabilidades para chutar qual a próxima palavra. Dessa forma, o ChatGPT consegue começar a construir sentenças, baseado nas probabilidades das palavras do seu dicionário particular.
Infelizmente, essa abordagem sozinha ainda não é suficiente para gerar um conteúdo que faça sentido. Em resumo, o conteúdo gerado dificilmente faria sentido, uma vez que as palavras são escolhidas aleatoriamente. Uma solução seria ter n-grams maiores e maiores, assim teríamos mais chances de encontrar tokens/palavras que façam sentido. No entanto, calcular n-grams é algo caro, e que escala rapidamente. Imagine a quantidade de combinações que precisam ser feitas para calcular a frequência de um par de caracteres (2-grams) em no texto? E uma 3-gram? (Resposta: 1.6 bi e 60tri, respectivamente, considerando somente as 40 mil palavras mais utilizadas do Inglês).
O que fazer, então?
Isso é assunto para outro texto.
Pequeno typo: dente (era para ser tende).