Da Análise de Sentimentos para o Reconhecimento de Emoções: Uma história PLN

Como podemos usar Machine Learning para identificar emoções em tendências nas Redes Sociais

Rodrigo Masaru Ohashi
Neuronio BR
10 min readJul 24, 2019

--

Nota 1: uma versão em inglês deste artigo pode ser encontrada em “From Sentiment Analysis to Emotion Recognition: A NLP story

Nota 2: Os textos estão em inglês, devido aos dados de treinamento terem sido obtidos nessa língua. É possível seguir a mesma metodologia descrita neste artigo para produzir os dados e treinar o modelo totalmente em português.

Introdução

Recentemente eu estive fazendo umas pesquisas sobre PLN (Processamento de Linguagem Natural, ou em inglês, Natural Language Processing), a qual estuda a interação entre as máquinas e a linguagem humana, e como usar ela no dia-a-dia. Existem diversos trabalhos em áreas como a tradução de textos (Google Tradutor), agentes de diálogo (Chatbots), classificação de texto (análise de sentimentos, classificação de tópicos), entre outros.

Sobre os dados, é possível se dizer que existe uma grande quantidade, considerando que milhões de posts são criados nas redes sociais a cada segundo. Se isso não for o suficiente, ainda temos livros, artigos e outras fontes.

As redes sociais se tornaram uma ferramenta onde qualquer um pode expressar seus sentimento e pensamentos. Além disso, é uma boa forma de se manter informado dos acontecimentos mundiais. Podemos utilizar seus dados para conseguir resultados interessantes.

Num primeiro momento, poderia ser uma ferramenta de análise de sentimentos onde você poderia dizer a polaridade de uma frase. Por exemplo, o texto a seguir, tirado de uma rede social:

🌞Happy Friday my Lovelies. Spread some #kindness, #love, and #joy as you journey through your day.✨ #Shine your light and make a difference.💫 All my love.❤️ #Bkind #LoveLouder https://t.co/DJ5ddBLYYR

Podemos dizer que tem uma conotação positiva. Pensando sobre aplicações, esse tipo de ferramenta pode servir de a análise de produtos em dados de redes sociais. Um outro uso poderia ser para ver como uma empresa é vista pelos usuários da rede.

A análise de sentimentos é uma boa ferramenta se quisermos apenas ter a polaridade de uma frase. Aqui, iremos mais a fundo, tentando prever a emoção que um post carrega.

Com o reconhecimento de emoções, podemos ter informações mais granulares, trazendo o que de fato o usuário estava sentindo no momento exato em que ele fez a postagem. Ela classifica os textos em categorias representando as emoções humanas.

A primeira coisa que precisamos são os dados.

Procurando os dados

Tweet de @TwitterSupport

Quando estamos trabalhando com análise de sentimentos, existe uma quantidade grande de datasets, como o IMDb Movie Dataset, com reviews do IMDb, e o Stanford Sentiment Treebank, com reviews do Rotten Tomatoes. A mesma coisa não acontece para emoções. Alguns datasets disponíveis não são muito confiáveis, como o da CrowdFlower/Figure Eight, já que ele utiliza um esquema crowd source, cada usuário manda um pedaço do conjunto de dados com os labels. Isso é um problema já que devem existir particularidades quanto ao processo realizado para definir qual a label apropriada para cada texto. Afim de contornar esse problema, baseado em alguns artigos (ver referências), vamos montar o nosso dataset com as emoções.

Primeiramente, nosso foco será no Twitter. Os dados podem ser pegos da API official. Você pode pegar as credenciais aqui. Usando a biblioteca tweepy, podemos acessar a API e buscar por qualquer termo. Ela retorna alguns dados, incluindo:

  • id: Integer. Número de identificação
  • created_at: Date Time. Time stamp com o tempo de criação
  • screen_name: String. Nome de usuário
  • text: String. Tweet (max. 140 caracteres)
  • full_text: String. Tweet (max. 280 caracteres)

Nossa tarefa é identificar as emoções num tweet, então os dados além dos textos são irrelevantes. Como você pode ver, existem 2 campos de texto: um com o limite antigo de caracteres e outro com o novo. Quando o tamanho é maior que 140 caracteres (ou seja, o limite antigo), o campo text fica cortado.

Um dos pontos negativos do campo full_text é que ele não suporta retweets. Assim, vamos excluí-los (os retweets) da análise.

Em aprendizado supervisionado, o dataset deve ter uma label que é usada para classificação. Infelizmente ela não temos isso diretamente da API…

Um jeito seria rotular os dados na mão, mas isso levaria muito tempo. E se pudéssemos extrair algo dos próprios textos?

Tweets #Emotivos

Nas redes sociais, um padrão comum são as hashtags. Geralmente, o usuário encurta suas intenções com elas:

I feel good today! #joy

Poderíamos extrair as emoções de algumas hashtags. Um bom começo seria usar o valor da emoção como hashtag (do exemplo, #joy).

Emojis? 😄😡😢😨

Um outro jeito é tirar as emoções dos emojis:

I can’t believe I lost my other shoe 😡

Como no caso das hashtags, elas podem expressar as intenções do usuário.

Diferentemente de quando estamos tuitando, não é tão simples escrever emojis num console. Para ajudar, existe uma biblioteca chamada emoji. Ela converte representações, como :thumbs_up:, no emoji 👍.

Achando mais emojis e hashtags

Depois de usar algumas hashtags e emojis conhecidos, podemos explorar nossos dados para achar mais termos de busca.

Vamos usar a emoção joy (alegria). Como o conjunto inicial de termos, podemos usar: #joy, a emoção em si, #excited, representando a animação da pessoa e os emojis😂 e😄, ambos usados no lugar de risadas.

Depois de um pouco de análise dos dados, temos as seguintes contagens de emojis:

Emojis usados nos dados coletados

As primeiras 2 linhas são dos termos inicias. Os outros podem ser usados futuramente.

Fazendo o mesmo para hashtags:

Hashtags utilizadas nos dados coletados

Conforme descobrimos mais termos, eles serão mapeados para alguma emoção, dentro de um arquivo que será usado para coletar mais tweets. Dessa forma nós vamos construir um dataset, até chegar numa quantidade razoável de exemplos.

Validando os dados usando a Análise de Sentimentos

Os dados coletados foram rotulados automaticamente. Para aumentar a confiança nas labels, é possível utilizar a análise de sentimentos para validação. Por exemplo, se um texto tem um rótulo zangado, esperamos que tenha um viés negativo. Além disso, vamos considerar que frases classificadas com uma mesma emoção devam ter resultados parecidos.

Como dataset de treino, vamos usar o Sentiment140, criado por graduandos na universidade de Stanford. Os dados foram coletados do Twitter, com as labels: positiva (4), negativa (0) e neutra (2).

É importante notar que isso não prova que as labels estão corretas, mas mostra inconsistências de forma automática.

Pré-processamento dos textos

A primeira coisa que devemos fazer é o pré-processar os textos, removendo trecho inúteis ou pouco significativos. Para limpar os tweets, faremos o seguinte:

  • Converter para minúsculo: Converter todos caracteres para minúsculo
  • Remover caracteres especiais: Remover links e nomes de usuários e transformar emojis em textos
  • Remover repetições: Remover repetições de caracteres (ex: whaaaaaat => what)
  • Remover Stop Words: Remover stop words comuns

Para o passo das palavras vazias, é importante manter negações (not, no, nor) para preservar a intenção.

Tokenizer

Depois de pré-processar, vamos transformar os textos em vetores. Para isso, existe uma classe do Keras, chamada Tokenizer. Ela pega nossos dados e transforma na representação desejada.

A representação pode ser um vetor one-hot (um valor mapeado para uma posição) ou baseado numa pontuação tf-idf. Vamos usar a segunda, com 10000 palavras.

Modelo

Agora definimos nosso modelo de aprendizado de máquina. Para isso vamos utilizar uma camada GRU (Gated Recurrent Unit). Para este caso, poderia ser um LSTM (Long short-term memory), mas a diferença neste caso não era grande.

Arquitetura da rede neural

A implementação com o Keras pode ser encontrada no repositório do GitHub, no final do artigo.

Basicamente, usamos uma rede comum para esse tipo de tarefa, treinando uma camada de Embedding junto. Poderíamos usar pesos pré-treinados para esta última camada, como o GloVe ou o fastText, mas como os dados do Twitter são diferentes, vamos treiná-la do 0.

Sobre os dados de saída, usaremos apenas os positivos (4) com valor 1, e os negativos (0) com valor 0. Assim podemos usar um classificador binário. Quanto mais negativo um tweet for, menor seu resultado será (próximo de 0)

Para melhorar os resultados, poderíamos mexer nos hiperparâmetros, como o tamanho do embedding e o tamanho da camada bidirecional.

Validando os termos de busca

Com o modelo treinado, é hora de prever a polaridade dos dados do Twitter. Usaremos o arquivo de relações entre os termos e as emoções para separar os dados em categorias.

Para nossa análise, usaremos a média, o máximo, o mínimo e o desvio padrão. Os resultados serão classificados de acordo com suas classes.

Vamos ver os dados coletados anteriormente (com a label joy):

Resultado da análise de sentimentos para os dados coletados

Agrupando os resultados e extraindo os valores, temos o seguinte:

Resultados agrupados da análise de sentimentos

Como você pode ver acima, o valor médio do resultado agrupado é mais positivo do que negativo. Isso era esperado, já que joy pode ser classificado como positivo.

O termo :face_with_tears_of_joy: (😂) tem um valor médio menor do que os outros. Comparando com o resultado agrupado, :grinning_face_with_smiling_eyes: (😁) também tem um resultados abaixo do esperado. Provavelmente esses 2 emojis são usados em outros contextos também. Por outro lado, as hashtags (#joy e #excited) possuem valores acima da média.

Vamos pegar apenas os valores que batem com as nossas expectativas, considerando o valor agrupado e a polaridade previamente conhecida (positiva para o caso em questão). Usaremos a mesma metodologia para todos os termos/emoções.

Conferindo os Dados

Depois de todo o pré-processamento e filtragem usando a análise de sentimentos, o dataset resultante tem a seguinte estrutura:

Estrutura do dataset rotulado com as emoções

As colunas que iremos focar são: label, contendo a emoção, e text, com o tweet.

Vamos conferir a contagem de cada emoção:

Contagem das labels

É sempre uma boa ideia treinar seus modelos com datasets balanceados. Se você ver alguma inconsistência, volte para o passo anterior e repita o processo de captura e análise dos dados, até conseguir um balanço entre os rótulos.

Descobrindo Emoções

Com o dataset de emoções, vamos para a tarefa final: construir um modelo capaz de descobrir a emoção no texto

Uma boa parte do trabalho feito na análise de sentimento pode ser usada aqui, com pequenas mudanças. Usaremos a mesma técnica para obter o tokenizer e o mesmo pré-processamento.

Sobre as labels, existe uma imagem famosa que representa as emoções humanas, chamada de Roda das Emoções de Plutchik.

Roda das Emoções de Plutchik

Como você pode ver, existe uma grande quantidade de emoções. Aqui, vamos deixar as coisas mais simples e usar apenas 4: joy, fear, sadness e anger. Iterando sobre o processo descrito anteriormente, é possível conseguir os dados para as outras emoções e criar um modelo capaz de descobri-las.

Modelo

Para a arquitetura do modelo de aprendizado de máquina, usaremos uma LSTM Bidirecional, junto de uma camada CNN (Convolutional Neural Network). A ideia é que a camada LSTM vai pegar a informação de contexto da frase e a CNN deve extrair características locais.

A arquitetura do modelo é a seguinte:

Arquitetura do modelo LSTM + CNN

Poderíamos mudar o tamanho da camada de Embedding, as unidades da LSTM e o tamanho do kernel e os filtros da CNN para conseguir melhores resultados.

Resultados

Com o modelo treinado, vamos verificar os resultados. Usaremos outro dataset para isso.

Contagem de labels no dataset de teste

Ele foi gerado usando o mesmo processo, descrito anteriormente

Usando nosso modelo, depois de prever as labels e comparando com as verdadeiras, geramos a seguinte matriz de confusão:

Matriz confusão para o modelo treinado

Como você pode ver, temos resultados promissores.

Testando nosso Modelo

Finalmente podemos testar nosso modelo. Vamos pegar um tópico em destaque no Twitter e usá-lo como o termo de busca. No momento em que eu escrevia esse artigo, a Kyoto Animation (também conhecida como KyoAni), um dos maiores estúdios de animação do Japão, foi incendiado, deixando pelo menos 33 mortos e muitos machucados. Fãs por todo o mundo estavam postando mensagens de apoio e lamentando o ocorrido.

Para o termo “kyoto animation”, é esperado que os resultados sejam com as labels “sadness” e “fear”. Depois de rodar o modelo nesses dados, temos:

Resultados para o termo “kyoto animation”

Como você pode ver, nossas expectativas vão de encontro com os resultados.

Melhorias

Existem alguns pontos que poderiam melhorar nosso modelo. Primeiro, os dados de validação poderiam ser revisados, um por um, para remover inconsistências e refletir melhor a realidade.

Outra coisa é que na filtragem pela análise de sentimentos, o método usado pode estar prejudicando os dados, e isso poderia ser melhorado iterando com valores diferentes e comparando os resultados.

Por fim, o número de exemplos no nosso dataset poderia ser maior. Assim, a precisão final poderia ser melhor

Conclusão

Fizemos 3 coisas aqui: um processo para construir um dataset de emoções; uma ferramenta de análise de sentimentos para validar os dados; um modelo de reconhecimento de emoções para prever o valor de emoção que um tweet carrega.

Com isso em mãos, poderíamos ter um modelo de review mais detalhado que uma simples análise de sentimentos. Além disso, é possível criar sistemas que ajudem o sistema de saúde a identificar problemas mentais, antes de algo mais grave.

Por último, como o problema pode ser interpretado como uma classificação de texto, o mesmo modelo poderia ser usado para classificar outros textos em diferentes tipos de categorias.

--

--

Rodrigo Masaru Ohashi
Neuronio BR

Computer Engineering - University of São Paulo. Software Engineer @ Creditas. Machine Learning enthusiast. I like ☕