Usando o BERT para Análise de Sentimentos

Como usar essa tecnologia que hoje é o Estado da Arte em Processamento de Linguagem Natural

Arthur Oliveira
7 min readMay 14, 2021

Se você está na área de Machine Learning, é bem provável que já tenha se deparado com termos como Attentions, Transformers, Elmo, Bert, etc., que atualmente estão entre as tecnologias mais elaboradas em PLN.

Recentemente, comecei a ler alguns artigos a respeito e vi que existe pouco material em Português sobre o tema. Aqui, vou fazer um post introdutório sobre o BERT, um gerador de word embeddings dependentes de contexto, bem como também apresentar como utilizá-lo em código.

Encare este post como um convite para que você estude mais a respeito do assunto. Algumas das imagens e trechos de código foram adaptados de postagens interessantes que me deparei nesse período e estão constando todas nas referências.

Let’s do it!

O que é o BERT?

Eu sou o Bert!

O BERT (Bidirectional Encoder Representations from Transformers) foi proposto em 2018 por pesquisadores do Google. Ele é uma nova técnica de Processamento de Linguagem Natural (PNL) e representou um grande avanço na área devido ao seu incrível desempenho.

Por muito tempo, a área de PLN enfrentou o desafio de qual seria a melhor forma de representar palavras e frases (com vetores numéricos — word embeddings) de modo que se mantenham significados e relacionamentos entre palavras (contexto). Representações ultrapassadas como term-frequency ou TF-IDF são incapazes de manter informações do contexto de uma palavra em um texto. Tal problema dificultava o treinamento dos classificadores que eram alimentados com essas representações.

Podemos usar os modelos do BERT para extrair características de textos (ou embeddings, isto é, representações numéricas de palavras) ou podemos ajustar esses modelos em uma tarefa específica, como análise de sentimentos e modelos que respondem a perguntas (como chat-bots).

Para clarear, vou dar um exemplo. Veja a figura a seguir.

Retirada de: https://jalammar.github.io/illustrated-bert/

Considere o BERT como uma “caixa-preta” que é alimentada com frases em sua entrada e que produz uma série de números que representam matematicamente essas frases. Esses números então servem de entrada para outro modelo (Classifier), que pode ser uma rede neural do tipo MLP, e que será treinado para classificar se uma determinada frase é, por exemplo, Spam ou Não-Spam. O mesmo pode ser adaptado para classificar o sentimento de uma sentença como Positivo ou Negativo, e daí teríamos um Analisador de Sentimentos.

Indo adiante, veja a figura a seguir.

Retirada de: https://jalammar.github.io/illustrated-bert/

Um pouco mais detalhadamente, veja que o nosso BERT é composto de sub-estruturas chamadas ENCODERs. Elas são responsáveis por gerar representações vetoriais para cada palavra de cada sentença que alimenta o modelo. A grande vantagem aqui é que cada vetor gerado representa a palavra em um dado contexto. Ou seja, as expressões “fui ao banco” e “me sentei no banco” gerariam vetores diferentes para a palavra “banco”, pois cada uma está aplicada em um contexto diferente.

Não irei um nível além nessa análise por ser muito extensa. Mas ainda vou destacar que, para o nosso caso, é importante notar que a toda frase que alimenta o BERT é adicionado a sequência de caracteres [CLS] ao começo, indicando que irá começar uma etapa de classificação da sentença. Depois de passar pelo modelo, esse mesmo token terá a sua representação vetorial e será ele que usaremos para alimentarmos o modelo (Classificador) de Análise de Sentimentos.

Note também que cada palavra que entrar no Bert deve ter um número único (Token) associado. Isso será importante no momento da implementação.

Implementação

O lado positivo do BERT é que sua implementação é aberta e podemos fazer Analisadores de Sentimentos que atingem ótimos resultados em poucos minutos!

Vou apresentar o código a seguir. Ao invés de ir explicando cada trecho, vou por o código de uma vez e deixar a explicação para os comentários do código. Se alguma biblioteca estiver faltando no seu ambiente, não se esqueça de usar pip install para instalá-la.

O conjunto de dados que usaremos neste exemplo é SST2, que contém frases de resenhas de filmes, cada uma rotulada como positiva (tem o valor 1) ou negativa (tem o valor 0).

Base de Dados
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
import torch
import transformers as ppb
import warnings
warnings.filterwarnings('ignore')
#Carregando os dados
df = pd.read_csv('https://github.com/clairett/pytorch-sentiment-classification/raw/master/data/SST2/train.tsv', delimiter='\t', header=None)
#Por questões de performance, vamos nos limitar a 2000 elementos. #Esse trecho pode ser alterado a seu gosto
batch_1 = df[:2000]
#Carregando o BERT e o Tokenizador
model_class, tokenizer_class, pretrained_weights = (ppb.DistilBertModel, ppb.DistilBertTokenizer, 'distilbert-base-uncased')
tokenizer = tokenizer_class.from_pretrained(pretrained_weights)
model = model_class.from_pretrained(pretrained_weights)
#Preparando os dados para o BERT
tokenized = batch_1[0].apply((lambda x: tokenizer.encode(x, add_special_tokens=True)))
#Padronizando todas as entradas para que tenham o mesmo tamanho
max_len = 0
for i in tokenized.values:
if len(i) > max_len:
max_len = len(i)

padded = np.array([i + [0]*(max_len-len(i)) for i in tokenized.values])
#Gerando máscaras nas sentenças para adaptar a entrada ao Bert
attention_mask = np.where(padded != 0, 1, 0)
#Rodando o modelo com o método MODEL
input_ids = torch.tensor(padded)
attention_mask = torch.tensor(attention_mask)

with torch.no_grad():
last_hidden_states = model(input_ids, attention_mask=attention_mask)
#capturando o resultado do token CLS
features = last_hidden_states[0][:,0,:].numpy()
#capturando os labels para treinar o modelo
labels = batch_1[1]
#Aqui, o BERT já está treinado e as sentenças já viraram seus vetores e estão armazenadas em features. A partir de agora, você pode treinar qualquer modelo utilizando-as. #Dividindo em conjunto de treino e teste e jogando para o modelo de Regressão Logística. Nesse caso, poderia ser qualquer modelo. Como exercício, tente jogar para uma MLP. Dica: use MLPClassifier.
train_features, test_features, train_labels, test_labels = train_test_split(features, labels)
lr_clf = LogisticRegression()
lr_clf.fit(train_features, train_labels)
#Avaliando o modelo nos dados de teste
lr_clf.score(test_features, test_labels)
[[[Saída]]] 0.824

Como você pode ver, praticamente sem fazer muito esforço temos um modelo com 82.4% de acurácia para Análise de Sentimentos. Recomendo que reveja o código e o texto até aqui antes de continuar. Algumas coisas podem ter ficado um pouco nebulosas, pois o conteúdo que prepara para o uso do BERT é um tanto quanto pesado e algumas coisas ficaram de fora nessa introdução. De qualquer modo, a implementação é relativamente tranquila: carregar os dados e o modelo e em seguida preparar os dados para o BERT. Isso nos dá uma série de vetores de características (features) que podem ser inseridos em um modelo que atua como Analisador de Sentimentos, que em nosso caso é uma Regressão Logística.

Um próximo passo aqui seria, a partir do modelo criado e testado, usar essa aplicação no mundo real para ver como ela se sai analisando sentimentos de avaliações de filmes em inglês.

Para aprofundar

Bora que tem mais!

Como você deve ter percebido, eu apenas tangenciei o tema no post. Esse é um tópico bem pesado para ser esgotado de forma tão breve. Recomendo como aprofundamento estudar Attentions e Transformers, bem como também se aprofundar na arquitetura do BERT.

Outro tópico interessante é como usar isso para fazer um Analisador de Sentimentos em Português. Aqui, eu aproveitei o código de um classificador em Inglês e futuramente talvez também traga sobre como fazer o mesmo em nossa língua. Essa é uma área interessante de pesquisa e a língua portuguesa ainda possui poucos recursos disponíveis.

As referências apresentadas a seguir vão do mais simples e didático ao artigo original. Have fun!

Referências

--

--

Arthur Oliveira

Professor da área de computação. Doutor pela UNICAMP. Graduado em Ciências da Computação e mestre pela USP.