Consegue uma Inteligência Artificial escrever como Guimarães Rosa?

Como usar uma Rede Neural Recorrente para construir um modelo de língua

Julia Pocciotti
Turing Talks
8 min readAug 1, 2021

--

João Guimarães Rosa

Talvez você já tenha visto alguns exemplos pela internet sobre IA' s ou bots treinados em milhares de textos para escrever como um autor específico ou replicar um gênero textual. Diferentes técnicas podem ser usadas para gerar esse tipo de resultado. Hoje, vamos ver como construir um modelo para "escrever" como João Guimarães Rosa utilizando Redes Neurais Recorrentes. Explicarei aqui o básico sobre esse tipo de Rede Neural, mas, se quiser se aprofundar sobre o assunto, recomendo o nosso Turing Talks sobre o tema:

Se quiser conferir o projeto diretamente, você pode acessar o repositório do github com o código completo, ou então o site com o gerador de texto aqui.

Antes de começarmos a falar sobre o nosso modelo, vamos antes ver um pouco melhor qual é a nossa tarefa em questão.

O que é um "Modelo de Língua"?

Um modelo de língua possui uma tarefa bem simples: prever a próxima palavra ou caractere em uma sentença. Qualquer tipo de modelo que esteja cumprindo com essa tarefa, ou sendo treinado para isso, pode ser caracterizado como um modelo de língua.

Exemplo de um modelo de língua em prática

Você com certeza já encontrou modelos de língua pela internet, por exemplo, quando estamos fazendo uma busca no Google e aparecem sugestões para terminar a frase, isso é um modelo de língua.

De maneira mais formal, um modelo de língua irá aprender a distribuição de probabilidades em uma sequência de palavras. Ou seja, dada uma sequência de palavras de tamanho m, o modelo atribui uma probabilidade P(w(m+1)|w(1), …, w(m)).

Os dados

Sobre o escritor

João Guimarães Rosa é sem dúvida um dos maiores escritores da nossa literatura. Além de escrever contos e romances, Rosa foi também médico e diplomata brasileiro.

João Guimarães Rosa

Suas histórias são ambientadas em meio ao sertão brasileiro, e dentre vários aspectos formais presentes em sua obra, destaca-se o seu trabalho com a linguagem, esta, marcada por inúmeros neologismos e falas regionais.

— Epa! Nomopadrofilhospritossantamêin! Avança, cambada de filhos-da-mãe, que chegou minha vez!…
E a casa matraqueou que nem panela de assar pipocas, escurecida à
fumaça dos tiros, com os cabras saltando e miando de maracajás, e Nhô
Augusto gritando qual um demônio preso e pulando como dez demônios soltos. (trecho do conto "A hora e a vez de Augusto Matraga")

Para construir o nosso modelo, iremos usar todos os livros publicados pelo autor, são eles: Sagarana, Corpo de Baile, Grande Sertão Veredas, Primeiras Estórias, Tutameia, Estas estórias e Ave, palavra. Os PDFs de todos as obras podem ser encontrados no site LeLivros.

Extração do texto do PDF

Tendo os PDFs dos nossos livros baixados, precisamos agora convertê-los para um arquivo .txt. Para isso, iremos utilizar a biblioteca textract:

Para simplificarmos o nosso código, iremos juntar todos os textos em apenas um único arquivo. Além disso, informações como notas iniciais da editora, informações de publicação e introduções feitas por outros autores foram retiradas manualmente no processo.

Lendo os dados

Com o texto preparado, vamos ler o arquivo o nosso arquivo e verificar algumas informações básicas, como o número de caracteres:

Vetorização do texto

Diferente de nós, humanos, os computadores não conseguem abstrair informações das palavras diretamente. Sendo assim, precisamos pensar em algum tipo de codificação numérica para que a máquina possa processar o nosso texto. Neste caso, iremos criar dois tipos de vetores numéricos:

  • char2idx: faz o mapeamento dos caracteres únicos para um índice
  • idx2char: faz o mapeamento dos números no índice para os caracteres

Aqui, criamos três representações importantes: temos o char2idx para codificar o nosso texto em uma representação numérica, o idx2char para decodificar o texto e text_as_int , que representa o nosso texto já codificado.

Redes Neurais Recorrentes (RNNs)

Por que Redes Recorrentes?

Ao lermos um livro, ou ao utilizar a linguagem de uma maneira geral, estamos levando em conta a ordem na qual os caracteres e as palavras são dispostas na página, ou seja, a ordem em que esses dados aparecem são extremamente importantes para a constituição do sentido do texto. Quando colocamos essa informação em conjunto, temos um texto coeso, do qual conseguimos extrair sentido.

Além da linguagem, podemos pensar em outros tipos de dados que também obedecem uma sequência, como informações de um eletrocardiograma, as bases nitrogenas em um DNA, ou séries temporais, por exemplo. Quando temos dados que são dispostos em uma ordem específica, utilizamos modelos sequenciais, como uma Rede Neural Recorrente (Recurrent Neural Network, em inglês — RNN), por exemplo.

Uma vantagem das RNNs é que com elas conseguimos treinar facilmente inputs e outputs com tamanhos diferentes e, principalmente, a nossa rede consegue aprender a generalizar as informações textuais independente da sua posição na frase, ou seja, com essa representação o modelo conseguiria prever que a palavra "Augusto" muito provavelmente teria a sua próxima palavra como "Matraga", independentemente de "Augusto" aparecer no início, meio, ou final da frase.

Como funciona uma RNN?

Em uma RNN tradicional, temos a seguinte representação:

Disponível em: https://stanford.edu/~shervine/teaching/cs-230/cheatsheet-recurrent-neural-networks

Como comentamos anteriormente, aqui estaremos lidando com dados que obedecem uma determinada sequência. Sendo assim, representaremos os nossos dados em "timesteps", e para fazermos previsões sequenciais cada y<t> (output) levará em conta não apenas o valor do seu timestep x<t>(input), mas também usará a informação de seu estado anterior a<t-1>.

Se você quiser saber mais informações sobre como funciona uma RNN, dê uma olhada no nosso Turing Talks sobre o tema aqui.

Preparando os dados para o modelo

Dado um caractere, ou uma sequência de caracteres, qual será o caractere mais provável? — Essa é a tarefa para a qual estaremos treinando o nosso modelo. Sendo assim, teremos uma sequência de caracteres como entrada do modelo e iremos prever o caractere seguinte.

Para isso, iremos dividir o texto em sequência de exemplos, e cada sequência terá um número de caracteres de tamanho determinado pela variável seq_length . Para cada sequência de entrada, os targets terão o mesmo comprimento, com exceção de um caractere deslocado para a direita. Dessa forma, a nossa rede ficará assim:

Para isso, vamos usar o método from_tensor_to_slices to módulo Dataset to TensorFlow para criar um um objeto desse tipo a partir do text_as_int , que já tínhamos definimos acima. Por fim, vamos separar os dados em batches para o nosso treinamento. Nessa representação, usaremos o comprimento de cada sentença de input limitada para 100 caracteres:

Em seguida, precisamos criar tuplas dessas sequências para alimentarmos a nossa RNN:

Por fim, vamos embaralhar o nosso dataset e dividi-lo em batches:

Construindo o modelo

Nessa representação, usaremos três camadas:

  • Embedding: Nossa camada de entrada. Se você não sabe o que são embeddings, você pode conferir o nosso Turing Talks sobre o tema aqui.
  • GRU (Gated Recurrent Unit): A nossa RNN. GRUs são um tipo especial de Redes Recorrentes, se quiser ver mais sobre elas, assista esse vídeo aqui.
  • Fully connected: A tradicional camada de pesos em uma rede neural.

Treinando o modelo

Para treinar a nossa rede, usaremos o otimizador "Adam" e a função de custo "Sparse Categorical Loss". Além disso, para conseguirmos carregar os nossos pesos e salvar a nossa performance, iremos configurar um diretório para os checkpoints:

Por fim, usaremos 10 épocas para fazer o nosso treinamento.

Para isso, recomendo que você utilize uma GPU. Se você não possui uma GPU no seu computador, você pode usar o Google Colab, e em Runtime>Change runtime type selecione "GPU".

Escrevendo como Guimarães Rosa

Para ver a posição do nosso último checkpoint, utilizamos a seguinte notação: tf.train.latest_checkpoint(checkpoint_dir)

Finalmente, podemos usar a função que definimos anteriormente para criar um novo modelo com batch_size=1 , e usar o método load_weights salvos do último checkpoint:

Agora o nosso modelo está pronto para fazer previsões! Tudo o que precisamos é criar uma função para processar o texto da mesma forma que fizemos anteriormente quando treinamos o modelo:

Agora basta escolher uma string e ver o seu modelo fazer previsões!

Exemplo de previsão com o modelo treinado

Fazendo mais previsões!

Agora que temos um modelo treinado, podemos usar plataformas como o Streamlit e o Heroku para fazer a versão com o deploy do nosso modelo.

Com o Streamlit conseguimos facilmente carregar o nosso modelo, construir uma interface para o usuário escrever a string inicial, e então fazer as previsões do texto. Por fim, utilizamos o Heroku para colocar o que construimos com o Streamlit em uma url compartilhável.

Com essas duas plataformas, chegamos em um resultado final assim:

Se quiser brincar com o modelo que construímos, basta acessar o site: http://gerador-texto-guimaraes.herokuapp.com/ .

Nesse Turing Talks não vamos entrar em detalhes sobre como fazer o deploy do modelo (quem sabe em um próximo texto!). Mas se você quiser ver o código, ele está todo disponível neste repositório:

Conclusão

É claro que os nossos resultados estão longe de gerar um texto como um conto ou obra de João Guimarães Rosa, mas justamente pela linguagem muito característica do autor, repleta de neologismos e marcada pelo falar popular do sertão, é curioso ver como o nosso modelo consegue replicar alguns desses padrões, gerando resultados, no mínimo, divertidos.

Usando uma representação como essa, conseguimos criar vários outros modelos língua com corpus diferentes. Podemos ensinar uma IA a escrever como Shakespeare, J. R. R. Tolkien, e muitos outros! Basta termos um dataset e tratá-lo para essa tarefa.

Se você gostou dessa abordagem, saiba que RNNs são apenas o começo dentre vários outros tipos de redes que podemos usar para construir modelos de língua. Hoje, temos modelos muito mais sofisticados para modelar a linguagem e gerar resultados ainda mais surpreendentes, como o GPT-2, GPT-3 e, o mais recente, Google MUM.

Por fim, não deixe de acompanhar o Turing USP no Facebook, Linkedin, Instagram e, claro, nossos posts do Medium!

Bons estudos e até a próxima!

“O real não está na saída nem na chegada: ele se dispõe para a gente é no meio da travessia.”

João Guimarães Rosa

Agradecimento especial à Luísa Mendes Heise pela ajuda na coleta do corpus

--

--