Clickbait ou Não? Usando Naive Bayes Para Classificar Títulos do Buzzfeed
Se tratando do Buzzfeed, pode ter certeza que a maioria é “1”
Trabalho atualmente como Analista de BI, e de uma brincadeira no trabalho surgiu a ideia de validar algumas justificativas através do Processamento de Linguagem Natural (PLN). Passei uma semana estudando e implementando o modelo num dataset que achei no Kaggle, e trago nesse artigo meu melhor restultado.
1. Pré-processamento dos Dados
1.1 Importando as bibliotecas
import pandas as pdfrom sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics import confusion_matrix, f1_score, classification_report, accuracy_score, roc_curve, auc
from sklearn.model_selection import train_test_split
from sklearn.externals import joblibimport matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
1.2 Tratando os datasets
De início existiam 2 datasets diferentes. O primeiro apenas com títulos considerados clickbait, e o segundo com os títulos de matérias sérias.
click_bait = pd.read_csv('clickbait_titles.csv')
serious_titles = pd.read_csv('non_clickbait_titles.csv')
Para os títulos clickbait adicionei uma coluna chamada “label”, com valor de 1 (positivo), e para os títulos sérios adicionei a mesma coluna, porém com valor 0 (negativo).
click_bait['label'] = 1
serious_titles['label'] = 0
Removi as colunas extras, que não seriam utilizadas pelo modelo, para que os datasets ficassem mais “limpos” e pudessem ser lidos de forma mais clara.
click_bait = click_bait[['title', 'label']]
serious_titles = serious_titles[['title', 'label']]
Depois disso concatenei ambos os datasets em um só, iniciando sua preparação para o modelo.
1.3 Preparando o dataset para treino/teste
O primeiro passo foi definir uma variável para a feature (título) e outra para a label.
X = titles['title']
y = titles['label']
Depois, usei a função CountVectorizer, do próprio Scikit Learn, para transformar o dataset numa matriz com a representação numérica de cada palavra.
cv = CountVectorizer()X = cv.fit_transform(X)
Por último, separei o dataset para que pudesse treinar e testar com uma quantidade razoável de dados.
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 42)
O tamanho padrão para os dados de teste é de 25% do dataset total, e esse número pode ser alterado passando o parâmetro test_size =
2. Criando o modelo
Antes de falar sobre a criação do modelo, queria dar uma explicação rápida sobre o Naive Bayes, o método principal utilizado.
Ele é uma técnica de classificação baseada no Teorema de Bayes com uma suposição de independência entre os preditores. Em termos simples, um classificador Naive Bayes assume que a presença de uma característica particular em uma classe não está relacionada com a presença de qualquer outro recurso.
O Teorema de Bayes fornece uma forma de calcular a probabilidade posterior
P (C | X) a partir de P ( C),
P (x) e P (X | c).
- P (c | x) é a probabilidade posterior da classe (c, alvo) dada preditor (x, atributos).
- P © é a probabilidade original da classe.
- P (x | c) é a probabilidade que representa a probabilidade de preditor dada a classe.
- P (x) é a probabilidade original do preditor.
2.1 Atribuindo o modelo
multinomial_clf = MultinomialNB(alpha = 1)
2.2 Treinando o modelo
multinomial_clf.fit(X_train, y_train)
2.3 Prevendo dados de teste
pred_mult = multinomial_clf.predict(X_test)
3. Checando a precisão do modelo
Para ter certeza de que o modelo foi bem sucedido, resolvi checar sua precisão de diversas maneiras diferentes.
3.1 Confusion Matrix
A Confusion Matrix mostra quantos exemplos, exatamente, foram previstos de forma correta ou não.
Por exemplo, 310 títulos foram marcados corretamente como Clickbait, enquanto 8 foram marcados de forma errada.
3.2 Precision-Recall-F1 Score
A Precision é a proporção de vp / (vp + fp), onde vp é a quantidade de verdadeiro positivos, e fp a quantidade de falso positivos. A Precisão é a habilidade do classificador de não marcar positiva uma amostra que é negativa.
O Recall é a proporção de vp / (vp + fn), onde vp é a quantidade de verdadeiro positivos, e fn a quantidade de falso negativos. O Recall é a habilidade do classificador de achar todos as amostras positivas.
O F1-Score é o valor da média entre Precision e Recall, onde o mesmo alcança seu melhor valor em 1, e o pior em 0.
3.3 ROC/AUC
A curva ROC, Receiver Operating Characteristic, mostra o quão bem o modelo criado pode distinguir entre duas coisas. Nesse caso, 0 e 1.
O valor do AUC, Area Under the ROC Curve, varia entre 0,0 e 1,0 e o limiar entre a classe é 0,5. Ou seja, acima desse limite, o algoritmo classifica em uma classe e abaixo na outra classe.
4. Validando o modelo com títulos atuais
Para a validação do modelo, abri o site do Buzzfeed e peguei 10 títulos que estavam na frontpage. 5 títulos clickbait, e 5 títulos não-clickbait.
4.1 Criando a lista com os títulos
Primeiro defini uma lista com os títulos que foram usados para validação, usando o CountVectorizer para transformar numa matriz com a representação numérica dos itens.
Criei também uma lista com a label de cada título, para checar a precisão.
val = cv.transform(['Escolha um ídolo do k-pop e nós indicaremos seu produto de beleza ideal',
'Senador gay coloca Augusto Aras contra a parede: "Tenho subfamília? Sou doente?"',
'Suas opiniões sobre estas tendências atuais da moda vão nos dizer se você faz parte da geração millennial ou da geração Z',
'“Outras Ágathas virão”, diz oposição sobre pacote de Moro',
'Planeje sua invasão à Área 51 e descubra qual E.T. você vai encontrar por lá',
'10 livros com cartas de amor de gente meio passional',
'“Recomendo que procure ajuda psiquiátrica”, diz Gilmar Mendes sobre Janot',
'O pior é ter que concordar com o Gilmar, dizem aliados que romperam com Janot',
'Cheesecake nunca é demais!',
'O Facebook confirmou que políticos podem publicar o que quiserem, seja falso ou não'])val_y = [1, 0, 1, 0, 1, 1, 0, 0, 1, 0]
4.2 Prevendo os títulos de validação e checando a precisão
Com os títulos já transformados da maneira necessária, bastou só usar o método predict.
val_pred = multinomial_clf.predict(val)
Para checar a precisão utilizei apenas o F1-Score, por ser simples e já dar uma ideia do Precision e Recall, e o resultado foi esse:
Conclusão
Para um primeiro modelo, acho que o resultado foi bem melhor que o esperado, mas ainda tem muito o que explorar nessa área de NLP.
O código, datasets e webapp que criei para o modelo estão no meu Github.