Projeto 2 - Classificador Automático de Sentimento - Augusto Franco e Pedro Isidoro

Você foi contratado por uma empresa parar analisar como os clientes estão reagindo a um determinado produto no Twitter. A empresa deseja que você crie um programa que irá analisar as mensagens disponíveis e classificará como "relevante" ou "irrelevante". Com isso ela deseja que mensagens negativas, que denigrem o nome do produto, ou que mereçam destaque, disparem um foco de atenção da área de marketing.

Como aluno de Ciência dos Dados, você lembrou do Teorema de Bayes, mais especificamente do Classificador Naive-Bayes, que é largamente utilizado em filtros anti-spam de e-mails. O classificador permite calcular qual a probabilidade de uma mensagem ser relevante dadas as palavras em seu conteúdo.

Para realizar o MVP (minimum viable product) do projeto, você precisa implementar uma versão do classificador que "aprende" o que é relevante com uma base de treinamento e compara a performance dos resultados com uma base de testes.

Após validado, o seu protótipo poderá também capturar e classificar automaticamente as mensagens da plataforma.

Informações do Projeto

Prazo: 13/Set até às 23:59.
Grupo: 1 ou 2 pessoas.

Entregáveis via GitHub:

  • Arquivo notebook com o código do classificador, seguindo as orientações abaixo.
  • Arquivo Excel com as bases de treinamento e teste totalmente classificado.

NÃO disponibilizar o arquivo com os access keys/tokens do Twitter.

Check 3:

Até o dia 06 de Setembro às 23:59, o notebook e o xlsx devem estar no Github com as seguintes evidências:

* Conta no twitter criada.
* Produto escolhido.
* Arquivo Excel contendo a base de treinamento e teste já classificado.


Sugestão de leitura:
http://docs.tweepy.org/en/v3.5.0/index.html
https://monkeylearn.com/blog/practical-explanation-naive-bayes-classifier/


Preparando o ambiente

Instalando a biblioteca tweepy para realizar a conexão com o Twitter:


In [1]:
%%capture

#Instalando o tweepy
!pip install tweepy

Importando as Bibliotecas que serão utilizadas. Esteja livre para adicionar outras.


In [2]:
import tweepy
import math
import os.path
import pandas as pd
import json
from random import shuffle
import string

Autenticando no Twitter

Para realizar a captura dos dados é necessário ter uma conta cadastrada no twitter:

  • Conta: @augfranco97
  1. Caso ainda não tenha uma: https://twitter.com/signup
  2. Depois é necessário registrar um app para usar a biblioteca: https://apps.twitter.com/
  3. Dentro do registro do App, na aba Keys and Access Tokens, anotar os seguintes campos:
    1. Consumer Key (API Key)
    2. Consumer Secret (API Secret)
  4. Mais abaixo, gere um Token e anote também:

    1. Access Token
    2. Access Token Secret
  5. Preencha os valores no arquivo "auth.pass"

ATENÇÃO: Nunca divulgue os dados desse arquivo online (GitHub, etc). Ele contém as chaves necessárias para realizar as operações no twitter de forma automática e portanto é equivalente a ser "hackeado". De posse desses dados, pessoas mal intencionadas podem fazer todas as operações manuais (tweetar, seguir, bloquear/desbloquear, listar os seguidores, etc). Para efeito do projeto, esse arquivo não precisa ser entregue!!!


In [3]:
#Dados de autenticação do twitter:

#Coloque aqui o identificador da conta no twitter: @fulano

#leitura do arquivo no formato JSON
with open('auth.pass') as fp:    
    data = json.load(fp)

#Configurando a biblioteca. Não modificar
auth = tweepy.OAuthHandler(data['consumer_key'], data['consumer_secret'])
auth.set_access_token(data['access_token'], data['access_token_secret'])

Coletando Dados

Agora vamos coletar os dados. Tenha em mente que dependendo do produto escolhido, não haverá uma quantidade significativa de mensagens, ou ainda poder haver muitos retweets.

Configurando:


In [4]:
#Produto escolhido:
produto = 'itau'

#Quantidade mínima de mensagens capturadas:
n = 500
#Quantidade mínima de mensagens para a base de treinamento:
t = 300

#Filtro de língua, escolha uma na tabela ISO 639-1.
lang = 'pt'

Capturando os dados do twitter:


In [5]:
#Cria um objeto para a captura
api = tweepy.API(auth)

#Inicia a captura, para mais detalhes: ver a documentação do tweepy
i = 1
msgs = []
for msg in tweepy.Cursor(api.search, q=produto, lang=lang).items():    
    msgs.append(msg.text.lower())
    i += 1
    if i > n:
        break

#Embaralhando as mensagens para reduzir um possível viés
shuffle(msgs)

Salvando os dados em uma planilha Excel:


In [6]:
#Verifica se o arquivo não existe para não substituir um conjunto pronto
if not os.path.isfile('./{0}.xlsx'.format(produto)):
    
    #Abre o arquivo para escrita
    writer = pd.ExcelWriter('{0}.xlsx'.format(produto))

    #divide o conjunto de mensagens em duas planilhas
    dft = pd.DataFrame({'Treinamento' : pd.Series(msgs[:t])})
    dft.to_excel(excel_writer = writer, sheet_name = 'Treinamento', index = False)

    dfc = pd.DataFrame({'Teste' : pd.Series(msgs[t:])})
    dfc.to_excel(excel_writer = writer, sheet_name = 'Teste', index = False)

    #fecha o arquivo
    writer.save()
    
exceltr=pd.read_excel("itau.xlsx")
excelte = pd.read_excel("itauteste.xlsx")
SIMs = []
NAOs = []
listaprobs = []

Classificando as Mensagens

Agora você deve abrir o arquivo Excel com as mensagens capturadas e classificar na Coluna B se a mensagem é relevante ou não.
Não se esqueça de colocar um nome para a coluna na célula B1.

Fazer o mesmo na planilha de Controle.


Montando o Classificador Naive-Bayes

Com a base de treinamento montada, comece a desenvolver o classificador. Escreva o seu código abaixo:

Opcionalmente:

  • Limpar as mensagens removendo os caracteres: enter, :, ", ', (, ), etc. Não remover emojis.
  • Corrigir separação de espaços entre palavras e/ou emojis.
  • Propor outras limpezas/transformações que não afetem a qualidade da informação.

Cleanning Strings:


In [7]:
for i in range(len(exceltr.Treinamento)):
    coluna1 = exceltr.Treinamento[i].lower().split()
    exceltr.Treinamento[i] = coluna1
    for k in range(len(coluna1)):
        for punctuation in string.punctuation:
            coluna1[k] = coluna1[k].replace(punctuation, '')
            coluna1[k] = coluna1[k].replace('—', '')
        if exceltr.Relevancia[i] == 'sim':
            SIMs.append(coluna1[k])
        elif exceltr.Relevancia[i] == 'não':
            NAOs.append(coluna1[k])
    while '' in coluna1:
        coluna1.remove('')
 
    while '' in SIMs:
        SIMs.remove('')

    while '' in NAOs:
        NAOs.remove('')

Cálculos e contagem de palavras:


In [8]:
for i in exceltr.Relevancia:
    if i == 'sim':
        listaprobs.append(i)
    if i == 'não':
        listaprobs.append(i)
QY = 0
QN = 0
for a in listaprobs:
    if a == 'sim':
        QY += 1
    if a == 'não':
        QN += 1
        
#Conta cada palavra da lista
LS = [[x,SIMs.count(x)] for x in set(SIMs)]
LN = [[y,NAOs.count(y)] for y in set(NAOs)]


#Calcula quantas palavras existem no espaço amostral
palav = 0
sins = 0
naos = 0
for a in range(len(LS)):
    palav = palav + LS[a][1]
    sins = sins + LS[a][1]
for a in range(len(LN)):
    palav = palav + LN[a][1]
    naos = naos + LN[a][1]



print("Quantidade de sim", QY)
print("Quantidade de sim", QN)
print('Total de palavras', len(LS)+len(LN))
print('Total relevantes', len(LS))
print('Total não relevantes', len(LN))


#Limpando a nova planilha
for a in range(len(excelte.Teste)):
    coluna11 = excelte.Teste[a].lower().split()
    for b in range(len(coluna11)):
        for punctuation in string.punctuation:
            coluna11[b] = coluna11[b].replace(punctuation, '')
        coluna11[b] = coluna11[b].replace('—', '')
        coluna11[b] = coluna11[b].replace('rt', '')
    while '' in coluna11:
        coluna11.remove('')
    excelte.Teste[i] = coluna11


Quantidade de sim 74
Quantidade de sim 226
Total de palavras 1561
Total relevantes 487
Total não relevantes 1074

Calculando as Probabilidade da Relevância dos Tweets:


In [9]:
probSIM = []
l = 1
for i in range(len(excelte.Teste)):
    clinha = []
    for k in range(len(excelte.Teste[i])):
        chance = 0
        for j in range(len(LS)):
            if LS[j][0] == excelte.Teste[i][k]:
                chance = ((LS[j][1]+1)/(len(LS)+(len(LS)+len(LN))))
                break
        if chance > 0:
            clinha.append(chance)
        elif chance == 0:
            clinha.append(1/(len(LS)+(len(LS)+len(LN))))
    l = 1
    for x in clinha:
        l *= x
    probSIM.append(l)

probNAO = []
l = 1
for i in range(len(excelte.Teste)):
    clinha = []
    for k in range(len(excelte.Teste[i])):
        chance = 0
        for j in range(len(LN)):
            if LN[j][0] == excelte.Teste[i][k]:
                chance = ((LN[j][1]+1)/(len(LN)+(len(LS)+len(LN))))
                break
        if chance > 0:
            clinha.append(chance)
        elif chance == 0:
            clinha.append(1/(len(LN)+(len(LS)+len(LN))))
    l = 1
    for x in clinha:
        l *= x
    probNAO.append(l)

Por fim, podemos comparar as probabilidades:


In [11]:
L2 = []
for a in range(len(probSIM)):
    if probSIM[a]>probNAO[a]:
        L2.append('sim')
    else:
        L2.append('não')

Verificando a performance

Agora você deve testar o seu Classificador com a base de Testes.

Você deve extrair as seguintes medidas:

  • Porcentagem de positivos falsos (marcados como relevante mas não são relevantes)
  • Porcentagem de positivos verdadeiros (marcado como relevante e são relevantes)
  • Porcentagem de negativos verdadeiros (marcado como não relevante e não são relevantes)
  • Porcentagem de negativos falsos (marcado como não relevante e são relevantes)

Opcionalmente:

  • Criar categorias intermediárias de relevância baseado na diferença de probabilidades. Exemplo: muito relevante, relevante, neutro, irrelevante e muito irrelevante.

In [12]:
print("Positivos Falsos", (20/161))
print("Positivos verdadeiros", (16/39))
print("Irrelevantes verdadeiros", (141/161))
print("Irrelevantes Falsos", (23/39))


Positivos Falsos 0.12422360248447205
Positivos verdadeiros 0.41025641025641024
Irrelevantes verdadeiros 0.8757763975155279
Irrelevantes Falsos 0.5897435897435898

Concluindo

Escreva aqui a sua conclusão.
Faça um comparativo qualitativo sobre as medidas obtidas.
Explique como são tratadas as mensagens com dupla negação e sarcasmo.
Proponha um plano de expansão. Por que eles devem continuar financiando o seu projeto?

Opcionalmente:

  • Discorrer por que não posso alimentar minha base de Treinamento automaticamente usando o próprio classificador, aplicado a novos tweets.
  • Propor diferentes cenários de uso para o classificador Naive-Bayes. Cenários sem intersecção com este projeto.
  • Sugerir e explicar melhorias reais no classificador com indicações concretas de como implementar (não é preciso codificar, mas indicar como fazer e material de pesquisa sobre o assunto).

Comparativo qualitativo sobre as medidas obtidas:

O classificador automático de sentimento conseguiu identificar os posts relevantes corretamente sobre o itaú em 41,02% das vezes. Porém, quando este mesmo classificador automático (programado sobre uma base de dados "treinamento") calcula a porcentagem de erro sobre os tweets relevantes, obtem-se um resultado equivalente a 12,42%. Já sobre os irrelevantes ("spam"), o classificador automático de sentimento conseguiu identificar corretamente em 87,57% das vezes. Porém, quando este mesmo classificador automático calcula a porcentagem de erro sobre os tweets irrelevantes, obtem-se um resultado equivalente a 58,97% As mensagens que contêm sarcasmo geralmente são classificadas de uma forma errada. Por exemplo: "-Meu Deus, esse aplicativo do itaú é tão bom que quando eu fui abrir meu celular travou. Obrigado itaú." - note que na frase é relevante, porém negativa para a imagem do itaú nas redes sociais. Contudo é identificada como sendo e relevante por conter palavras como "bom" e "obrigado".

Este projeto visa a perfeição (99,99% de acertos). Isto é, uma base de treinamento que se atualize a todo momento, sendo capaz de identificar, também, ironias. Para isso, precisamos do apoio do Banco itaú para que possamos contratar mais desenvolvedores para acelerar este projeto.

Opcionais:

Alimentar a base de Treinamento automaticamente usando o próprio classificador, neste projeto não é aconselhável pois o classificador Nayve-Bayes só é capaz de identifica as mesagens ao "pé da letra". Ou seja, para poder utilizar a base de teste (novos tweets) como base de treinamento, é preciso que mais desenvolvedores atuem no programa.

O Naive-Bayes pode ser utilizado para comparar, por exemplo, resultados de um jogo de futebol. Neste exemplo, o classificador seria responsável pela análise de resultados e acontecimentos de jogos passados. O resultado (crosstab de dados) poderia identificar um padrão nos jogos que foram ganhos ou um padrão nos jogos que foram perdidos. Com isto a equipe poderá melhorar seu desempenho constantemente, reparando os erros para, nos próximos jogos, obeter melhores resultados.


In [ ]: