Desenvolvimento

16 mai, 2017

Análise de sentimentos – Aprenda de uma vez por todas como funciona utilizando dados do Twitter

Publicidade

Você sabe o que é análise de sentimentos?

Imagine uma empresa que quer saber sobre a reputação de um produto que foi lançado no mercado. É possível usar as redes sociais para buscar informações; mas como saber se estão falando bem ou mau desse produto? Ler, entender e tirar uma conclusão de centenas de comentários de usuários pode ser uma tarefa inviável.

A análise de sentimentos é uma técnica que consiste em extrair informações de textos em linguagem natural. O objetivo dessa técnica é obter, de forma automática, a polaridade de um texto ou sentença. Por exemplo, dada uma sentença, um computador classifica-a como positiva ou negativa. Ou ainda, dado um texto, o computador classificar como positivo ou negativo o conteúdo.

Muitas empresas e startups fazem análise de sentimentos de opiniões nas redes sociais. O objetivo é saber o humor dos seus produtos ou de uma marca, por exemplo. Nesse artigo, vamos abordar essa técnica sendo aplicada em dados do Twitter.

Estamos trabalhando em um projeto juntamente com o governo de Minas Gerais, e esses dados foram coletados e rotulados por pessoas da equipe que trabalha para o Estado. Além de aprender sobre essa técnica, você poderá fazer download da base de dados e os scripts utilizados nesse artigo.

Hoje, você vai aprender mais sobre:

  • Base de dados e scripts
  • Pré-processamento do texto
  • Modelando a base de dados
  • Naive Bayes – como funciona
  • Sentiment Analysis hands on!
  • Avaliando o modelo – resultados
  • Melhorando o modelo
  • Cases de sucesso

Base de dados e scripts

Nossa base de Tweets contém 8199 linhas classificadas como positivo, negativo e neutro. Você pode fazer download dela aqui

Para acompanhar tudo que foi feito nesse artigo, disponibilizo aqui o notebook com todos os scripts utilizados.

Aprecie sem moderação 🙂

Pré-processamento

O pré-processamento do texto consiste em alterar a estrutura da base de dados original para um formato mais conveniente para o processamento computacional.

Uma das tarefas muito utilizadas no pré-processamento de textos é a remoção de stopwords. Esse método consiste em remover palavras muito frequentes, tais como “a”, “de”, “o”, “da”, “que”, “e”, “do” entre outras, pois estas muitas vezes não representam informações relevantes para construção do modelo.

Outra tarefa muito utilizada é a stemming, que reduz o termo ao seu radical, removendo afixos e vogais temáticas. Por exemplo, a palavra “frequentemente” após esse processo, se torna “frequent”. A palavra “copiar” se torna “copi”. Uma vantagem do pré-processamento é a diminuição das palavras da base de dados. Consequentemente, temos um vocabulário menor de palavras no nosso modelo. Outra vantagem é que com menos palavras para processar, temos um ganho no tempo computacional para geração do modelo

Pré-processamento de textos e análise de sentimentos

Para a tarefa de análise de sentimentos, qualquer informação que levam a um entendimento do sentimento de uma frase é importante. Por exemplo, a frase “Eu não gosto do partido, e também não votaria novamente nesse governante!”. Podemos perceber que está claro que o advérbio não faz toda a diferença na sentença. Este está negando que gosta de um partido e que não votaria novamente. Está claro que a frase é negativa.

Porém, métodos de stopwords removem palavras muito usadas, distorcendo o sentido das frases. Veja como ficaria essa frase após um método de stopwords: ‘Gosto partido votaria novamente nesse governante”. A frase ficou com um outro sentido, o advérbio não foi removido distorcendo completamente o sentimento da frase.

Para a tarefa de análise de sentimentos, não iremos remover stopwords, e nem fazer stemming, pois essas técnicas afetam o desempenho do classificador de forma negativa. Vou disponibilizar as funções para remoção de stopwords e stemming, caso queira fazer testes. 

Naive Bayes

A tarefa de análise de sentimentos pode ser feita de várias formas. Nesse artigo, iremos usar Machine Learning usando aprendizado supervisionado (caso tenha alguma dúvida sobre esse assunto, leia este artigo).

Vamos usar o Algoritmo Naive Bayes para fazer a tarefa de análise de sentimentos. Ele é um classificador probabilístico simples, baseado no teorema de Bayes e utiliza dados de treino para formar um modelo probabilístico baseado na evidência das features no dado.

O Naive Bayes supõe que há uma independência entre as features do modelo. Isso significa que o classificador assume que a presença de uma determinada feature não tem nenhuma relação com outras. No caso de um texto, o classificador assume que as palavras não têm uma relação entre elas, por exemplo, na frase “I love my dog, but today it’s terrible”, o classificador assume que as palavras dessa frase não dependem uma da outra. Sendo assim, essa sentença pode ter uma polaridade positiva, caso a maioria das palavras tenham uma probabilidade maior de serem positivas. É por isso que este tem o nome de “Naive”.

Apesar dessa característica, o Naive Bayes funciona muito bem para classificação de textos.

Como o Naive Bayes funciona?

Vamos entender com um exemplo simples. Abaixo temos uma base de palavras com as suas respectivas classes.

O objetivo aqui é classificar se a palavra “love” é positiva ou negativa. Veja:

Base de dados:

Palavra Classe
Dog Positive
Love Negative
Bad Negative
Love Positive
Love Positive
Dog Positive
House Positive
Cat Positive
Cat Negative
Cat Negative
Love Negative
House Positive
Love Positive

O primeiro passo é criar uma tabela de frequência das palavras e suas classes.

Tabela de frequência:

Palavra Positive Negative
Dog 2 0
Love 3 2
Bad 0 1
Cat 1 3
House 2 0
Total 7 6

Agora, crie a tabela de probabilidades:

Palavra Positive Negative
Dog 2 0

2/13 = 0.15

Love 3 2

5/13 = 0.38

Bad 0 1 1/13 =0.076
Cat 1 3

4/13 = 0.30

House 0 2

2/13 = 0.15

Total 7 6
7/13 = 0.53 6/13 = 0.46

Agora, vamos calcular a probabilidade da palavra “love” ser positiva ou negativa.

O Naive Bayes usa a seguinte sentença:

  • P(positive|’love’) = P(‘love’|positive) * P(positive) / P(‘love’)
  • P(negative|’love’) = P(‘love’|negative) * P(negative) / P(‘love’)

Calculando:

  • P(‘love’|positive) = 3/7 = 0.42 , P(positive) = 7/13 = 0.53, P(‘love’) = 5/13 = 0.3
  • P(‘love’|negative) = 2/6 = 0.33 , P(negative) =6/13 = 0.46, P(‘love’) = 5/13 = 0.38

Agora, P(positive|’love’) = 0.42 * 0.53 / 0.38 = 0.58

Agora, P(negative|’love’) = 0.33 * 0.46 / 0.38 = 0.39

Veja que a probabilidade da palavra ‘love’ ser positiva é maior do que a probabilidade dela ser negativa.

Para classificar uma instância de teste, a probabilidade de cada palavra é calculada como no exemplo acima, caso a palavra exista no treino.

Caso o classificador não “conheça” nenhuma palavra da instância de teste, este usará a probabilidade da classe de maior frequência no treino.

Bibliotecas utilizadas

Vamos usar as seguintes bibliotecas  para implementação da nossa solução:

  • nltk
  • scikit-learn
  • pandas
  • re

Caso não tenha instalado, consulte este artigo para instalar em seu ambiente.

Aqui vou importá-las de uma só vez para simplificar o processo, mas ao longo do artigo cada uma será explicada conforme sua utilização.

import nltk
import re
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
from sklearn.model_selection import cross_val_predict

Modelando a base de dados

Existem diversas formas de modelar uma base de dados de texto. A mais conhecida é chamada de Bag of Words. O modelo baseado em Bag of words utiliza todas as palavras do texto, a frequência de cada palavra e associa isso a classe do treino. A frequência das palavras é usada para treinar um modelo. Por exemplo, imagine as seguintes sentenças:

  • Sentença 1: O smartphone é bom, porém não gosto de smartphone grandes , Negativo
  • Sentença 2: Gostei bastante desse smartphone, mesmo sendo caro, Positivo

O modelo nesse caso, usa todas as palavras da base, ou seja, todas as palavras das duas sentenças: {o, smartphone, é, bom, porém, não, gosto, de, grandes, gostei, bastante, desse, mesmo, sendo, caro}

Em seguida, é feita a contagem do número de vezes que cada palavra apareceu na sentença. Para representar a Sentença 1, a palavra “smartphone” ficaria com a contagem 2, pois esta apareceu duas vezes na frase. As outras palavras ficaram com a contagem 1 e 0 quando essas não foram encontradas na sentença. Veja:

{o, smartphone, é, bom, porém, não, gosto, de, grandes, gostei, bastante, desse, mesmo, sendo, caro}

{1,2,1,1,1,1,1,1,1,0,0,0,0,0,0, Negativo}

A sentença 2 seria representada da seguinte forma:

{0,1,0,0,0,0,0,0, 0, 1,1,1,1,1,1,1, Positivo}

Sentiment Analysis hands on!

Primeiramente, vamos ler a base de dados e contar quantas linhas contêm nesta:

dataset = pd.read_csv('tweets-mg.csv)
dataset.count()

Para modelar a base, primeiramente é preciso separar os tweets das classes:

Para isso, faça:

tweets = dataset['Text'].values
classes = dataset['Classificacao'].values

Em seguida vamos criar o código que treina o modelo usando a abordagem Bag of Words e o algoritmo Naive Bayes Multinomial:

vectorizer = CountVectorizer(analyzer="word")
freq_tweets = vectorizer.fit_transform(tweets)
modelo = MultinomialNB()
modelo.fit(freq_tweets,classes)

Na linha 1, criamos um objeto do tipo CountVectorizer chamado vectorizer. Após isso, na linha 2, usamos o objeto vectorizer para calcular a frequência de todas as palavras da lista de tweets e armazenamos seu retorno em freq_tweets. Em seguida, criamos um objeto chamado modelo do tipo Naive Bayes Multinomial. Esse é o algoritmo de classificação que iremos utilizar.

Por fim, na linha 4, treinamos o modelo usando a frequência de palavras (freq_tweets) e as classes de cada instância.

Com o modelo já criado, vamos fazer alguns testes de classificação. Defina algumas instâncias, dentro de uma lista, como no exemplo abaixo:

testes = ['Esse governo está no início, vamos ver o que vai dar',
         'Estou muito feliz com o governo de Minas esse ano',
         'O estado de Minas Gerais decretou calamidade financeira!!!',
         'A segurança desse país está deixando a desejar',
         'O governador de Minas é do PT']

Em seguida, vamos transformá-las no formato bag of words e passar para nosso modelo classificar.

freq_testes = vectorizer.transform(testes)

modelo.predict(freq_testes)

Na primeira linha, armazenamos lista de frequências e na linha seguinte executamos a função predict para retornar uma classe para nossas instâncias de teste. Veja a saída:

array([‘Neutro’, ‘Neutro’, ‘Negativo’, ‘Negativo’, ‘Neutro’]

Podemos ver que nosso classificador errou na segunda instância, onde a classe deveria ser Positivo.

Em compensação, nas outras frases este se saiu bem. 🙂

Tente outras instâncias e observe o resultado do classificador.

Avaliando o modelo

O teste acima é interessante apenas para ver como a coisa funciona na prática.

Precisamos validar o modelo com dados que o modelo não conhece, para termos uma noção de como ele vai se sair em um sistema real.

Podemos separar parte dos dados para treino e parte para teste. Por exemplo, nossa base tem 8199 linhas. Poderíamos separar 80% desta para treinar o modelo e os outros 20% para testá-lo. Dessa forma, o modelo seria testado com dados que não conhece.

A desvantagem dessa abordagem é que, caso os 20% dos dados sejam muito parecidos com a parte do treino, teríamos um ótimo resultado. Mas isso pode não refletir a realidade na qual o modelo será usado. Existe uma forma melhor de validar o modelo criado.

Cross Validation

A forma mais eficiente é aplicar a técnica de Cross Validation, ou Validação Cruzada. Esta consiste em dividir todo o dado em K partes, que serão chamadas de folds. Dessas partes, uma será separada para teste e as outras restantes serão usadas para treinar o modelo.

Isso é feito repetidamente até que o modelo seja treinado e testado com todas as partes (folds).

Para entender, veja esse exemplo simples:

Para k = 10 , imagine que todo nosso dado de treino foi dividido em 10 partes distintas.

Assim, o modelo será treinado com 9 partes, e testado com a parte restante. Esse processo é repetido até que o modelo seja treinado e testado com todas as partes do dado. Com essa técnica, evitamos problemas de variância nos dados.

Para implementar a validação do modelo, vamos usar a função cross_val_predict(), também disponível na biblioteca Scikit-Learn:

resultados = cross_val_predict(modelo, freq_tweets, classes, cv=10)

O comando atribui a variável resultados à validação usando a técnica Cross Validation com 10 folds.

A primeira métrica que vamos medir é a Acurácia do modelo, que é, basicamente o percentual de acertos que o modelo teve. Para ver a acurácia faça:

metrics.accuracy_score(classes,resultados)
0.8831564824978656

Perceba que o modelo teve 0.88 de acurácia, isso significa que este acertou 88% dos testes.

Métricas de classificação

Existem outras métricas que nos dizem mais sobre os resultados.

As métricas precision (precisão), recall (revocação) e f1-score (medida F) nos dá uma visão melhor dos resultados e permite que entendamos melhor como o modelo está funcionando.

Para entender o que é a Precisão e Revocação e F1-Score, primeiro temos que saber sobre a terminologia para classificação. São elas:

  • True positive (TP): significa uma classificação correta da classe positive. Por exemplo, a classe real é Positivo e o modelo classificou como Positivo.
  • True negative (TN): significa uma classificação correta da classe negative. Por exemplo, a classe real é Negativo e o modelo classificou como Negativo.
  • False positive (FP): significa uma classificação errada da classe positive. Por exemplo, a classe real é Negativo e o modelo classificou como Positivo.
  • False negative (FN): Significa uma classificação errada da classe negative. Por exemplo, a classe real é Positivo e o modelo classificou como Negativo.

A precisão (precision) é calculada da seguinte forma:

precision = true positive / (true positive + false positive)

Isso significa o número de vezes que uma classe foi predita corretamente dividida pelo número de vezes que a classe foi predita.

Por exemplo, o número de vezes que a classe Positivo foi classificada corretamente dividido pelo número de classes classificadas como Positivo.

A revocação (recall) é calculada da seguinte forma:

recall = true positive / (true positive + false negative)

Isso significa o número de vezes que uma classe foi predita corretamente (TP) dividido pelo número de vezes que a classe aparece no dado de teste (FN).

Por exemplo, o número de vezes que a classe Positivo foi predita corretamente dividido pelo número de classes Positivo que contém no dado de teste.

Já a F1-score é calculada da seguinte forma:

f1-score = 2*((precision * recall) / (precision + recall)

Essa medida é a média harmônica entre precisão e revocação. Com essa informação, podemos dizer a performance do classificador com um indicador apenas.

Como essa medida é uma média, ela dá uma visão mais exata da eficiência do classificador do que apenas a precisão ou a revocação.

E a acurácia mostrada acima? Para calculá-la, use a formula:

Accuracy = (TP +TN )/ (TP + FP +TN +FN)

Perceba que a acurácia nos mostra como o classificador se saiu de uma maneira geral, pois, esta mede a quantidade de acertos sobre o todo. Por isso, podemos dizer que seria o percentual de instâncias classificadas corretamente.

Matriz de confusão

Essa matriz, de confusa, só tem o nome. Também conhecida como tabela de contingência, esta é uma matriz simples que apresenta os resultados de um classificador de forma intuitiva.

Positivo Negativo
Positivo 10 3
Negativo 4 6

Sobre o topo da tabela, temos as classes preditas pelo classificador; no lado esquerdo, temos as classes reais. Para entender, imagine que temos 23 instâncias para classificar em nossa base de teste. Dessas, 13 são classes positivas e 10 são classes negativas. Após o teste do modelo, imagine que a nossa Matriz de Confusão seja a tabela acima.

Com essa tabela, fica fácil ver que para a classe “positivo”, o classificador classificou 10 instâncias como positivo e 3 como negativo, ou seja, ele acertou 10 e errou 3 instâncias. Para a classe “negativo”, o classificador classificou 4 como positivo e 6 como negativo, ou seja, este acertou 6 e errou 4.

Para medir a Precisão, Revocação e Medida F do modelo faça:

sentimento=[‘Positivo’,’Negativo’,’Neutro’]

print (metrics.classification_report(classes,resultados,sentimento),”)

Precision Recall F1-score  Support
Positivo 0.95 0.88 0.91 3300
Negativo 0.89 0.93 0.91

2446

Neutro 0.80 0.84 0.82 2453
avg / total 0.89 0.88 0.88

8199

O pacote metrics exibe as medidas de precisão, revocação e medida F por classe e, por fim, a média disso.

Perceba que isso é útil para investigarmos a eficiência do modelo para classificação de classes específicas. Por exemplo, podemos ver que a precisão e a revocação do modelo para classificar instâncias neutras é bem diferente para as classes positivas e negativas.

Outro recurso interessante é a Matriz de Confusão. Vamos gerar esta para o nosso modelo:

print (pd.crosstab(classes, resultados, rownames=['Real'], colnames=['Predito'], margins=True), '')
Predito
Negativo  Neutro  Positivo  All
Real
Negativo 2275  162  9  2446
 Neutro  240  2067  146  2453
 Positivo  45  356  2899  3300
 All  2560  2585  3054  8199

Melhorando o modelo

Podemos tentar melhorar nosso modelo e existem várias formas de fazer isso. Podemos alterar o algoritmo de classificação, ou extrair mais features dos dados para treinar de uma forma diferente. Ou ainda, podemos ajustar os parâmetros do Naive Bayes.

Vamos fazer um teste alterando a forma de treinar nosso modelo. Vamos treiná-lo usando a modelagem “bigrams”. Essa modelagem consiste em passar duas palavras como features para o classificador ao invés de apenas uma. Dessa forma, estamos dizendo que uma palavra tem uma relação com outra palavra, veja um exemplo:

Na frase: “Eu não gosto desse governo”, na modelagem inicial, passamos para o modelo cada palavra sendo uma feature, ficaria = {eu, não, gosto, desse, governo}

Usando Bigrams, passaríamos para o modelo 2 palavras, veja:

{eu não, não gosto, gosto desse, desse governo}

Vamos treinar o modelo com essa modelagem e avaliar os resultados:

O código abaixo treina o modelo:

vectorizer = CountVectorizer(ngram_range=(1,2))
freq_tweets = vectorizer.fit_transform(tweets)
modelo = MultinomialNB()
modelo.fit(freq_tweets,classes)

A única diferença aqui é na linha 1, estamos usando a função ngram_range(1,2) que faz essa modelagem para nós.

Avaliando os resultados novamente

Imprima a acurácia desse novo modelo:

resultados = cross_val_predict(modelo, freq_tweets, classes, cv=10)
metrics.accuracy_score(classes,resultados)
0.89547505793389437

A acurácia melhorou! 🙂

Isso é bom, mas vamos investigar como está a classificação de cada classe. Vamos imprimir precisão, revocação e medida f1:

sentimento=['Positivo','Negativo','Neutro']
print (metrics.classification_report(classes,resultados,sentimento))
Precision Recall F1-score  

Support

Positivo 0.97 0.88 0.92 3300
Negativo 0.91 0.93 0.92 0.92
Neutro 0.80 0.89 0.84 2453
avg / total    0.90 0.90 0.90 0.90

Podemos ver que o medida f1 média também melhorou.

Agora, vamos imprimir a Matriz de Confusão:

print (pd.crosstab(classes, resultados, rownames=['Real'], colnames=['Predito'], margins=True))
Predito
Negativo Neutro Positivo All
Real
Negativo 2265 179 2 2446
Neutro 181 2177 95 2453
Positivo 43 357 2900 3300
All 2489 2713 2997 8199

Podemos ver que o resultado foi interessante, já que a medida f1 do modelo melhorou com essa modelagem. E visualizando a tabela de contingência, é possível ver que o modelo classificou melhor as instâncias neutras.

Veja: com Bigrams, as instâncias neutras foram classificadas 2177 vezes corretamente, contra 2067 e diminuiu o erro de instâncias neutras sendo classificadas como Negativo – 181 contra 240.

As instâncias negativas foram melhores classificadas com relação ao positivo: apenas 2 foram classificadas erroneamente como positivo. Porém, houve um pequeno aumento de instâncias negativas sendo classificadas como neutras: 179 contra 162.

É importante avaliar o modelo usando todas as métricas possíveis.

Acima, vimos que o modelo melhorou sua eficiência, visto que a classificação de instâncias neutras foram consideravelmente melhores.

Cases de sucesso

Análise de sentimentos tem se tornado uma técnica importante, especialmente em redes sociais, com o desenvolvimento de aplicações para monitoramento de produtos e marcas, assim como a análise de opiniões de clientes.

Algumas startups e empresas já estão usando análise de sentimentos em seus negócios. Um exemplo disso é o Hugme, uma ferramenta focada em monitoramento das redes sociais. Ela é totalmente integrada ao ReclameAqui e diversas mídias sociais. No Hugme, análise de sentimentos é aplicada na interação do usuário com o canal de atendimento da empresa.

Outro exemplo interessante é startup TrustVox, que tem como objetivo coletar a opinião de clientes que adquiriram um produto da  empresa. A startup usa a análise de sentimentos no feedback do cliente, e entrega diversas informações úteis para as empresas que utilizam esse serviço.

Ainda no segmento de atendimento ao cliente, temos a TrackSale, que obtém o feedback de clientes através de múltiplos canais de coleta de opiniões. Com as informações coletadas, a empresa aplica a análise de sentimentos nos dados e entrega uma plataforma para os clientes fazerem a gestão da satisfação dos clientes. A solução é bem interessante e tem muitas funcionalidades. Vale a pena conhecer.

Outro belo exemplo é o projeto Observatório do Investimento, que utiliza análise de sentimentos para identificar o humor de usuários sobre notícias relacionadas ao mercado financeiro. São coletadas notícias sobre ativos da bolsa de valores e, com esses dados, a técnica de análise de sentimentos é aplicada para se descobrir a polaridade das notícias; definindo, assim, o humor médio daquele ativo.

Oportunidades

Como você pode usar análise de sentimentos para o seu negócio? Imagine a análise de sentimentos aplicada a uma conversa com um assistente virtual que poderia descobrir o humor do cliente e o direcionar para um atendimento mais especializado?

Outro exemplo, seria uma ferramenta que fizesse crawler na Web em busca de opiniões sobre produtos de uma marca. Com os dados obtidos, pode-se aplicar análise de sentimentos em busca de obter um sentimento médio sobre produtos. Isso seria algo bem útil para empresas que buscam melhorar seus produtos e serviços.

Imagine como deve ser difícil para uma produtora de filmes saber qual é a opinião de milhares de pessoas que assistiram um determinado filme. Como atualmente existem diversos canais de discussão, sites especializados e redes sociais específicas, é possível obter comentários de centenas de pessoas sobre filmes. Com esses dados, a produtora pode aplicar a técnica de análise de sentimentos para classificar centenas de milhares de comentários de forma automática.

Conclusão

A análise de sentimentos é uma tarefa extremamente poderosa e pode ser utilizada em diversas aplicações em diversos segmentos diferentes. Saber corretamente como e onde aplicar essa técnica pode ser um diferencial fundamental para o sucesso do projeto.

Neste artigo, mostramos como utilizar uma das abordagens para realizar a análise de sentimentos em dados do Twitter sobre o Governo de Minas. Vimos como modelar e processar os dados, entendemos o funcionamento e aplicamos o algoritmo de classificação Naive Bayes. Por fim, avaliamos as métricas do nosso modelo.

Se você gostou desse artigo, compartilhe com seus amigos e deixe seu comentário aqui abaixo.