NLP & Código para todos | Função de perda ponderada para classificação de texto (multiclasse)
Este artigo explica por que e como usar uma função de perda ponderada para treinar um modelo de classificação de texto.
Notebook: Text_Classification_on_GLUE_with_weighted_Loss.ipynb
Uma das tarefas mais comuns em NLP
Conscientemente ou não, uma das atividades intelectuais mais frequentes nos negócios é a classificação de textos. De fato, quase tudo pode ser resolvido na forma de uma classificação de textos (veja esta lista não exaustiva de Hugging Face):
- classificar um texto de acordo com seu sentimento (positivo, negativo, neutro)
- classificar uma frase ou texto em categorias (por exemplo, de acordo com seu assunto principal)
- classificar um email como spam
- comparar 2 textos (paráfrases, causa/consequência)
- classificar um texto de acordo com sua linguagem
- classificar um texto de acordo com sua qualidade gramatical
- classificar uma resposta a um texto como correta ou não
- etc.
Solução técnica: modelos de linguagem natural
Os modelos de linguagem natural de Deep Learning fornecem uma solução técnica poderosa para classificar textos automaticamente.
Esses são modelos que foram treinados com bancos de dados textuais gigantes para aprender as características de uma linguagem. Então, adaptando os modelos à classificação de texto, obtemos uma solução técnica.
Esse ajuste é realizado usando um conjunto de dados rotulado. Por exemplo, para criar um modelo de classificação de sentimentos para avaliações de produtos em um site, precisamos de um conjunto de dados em que cada avaliação seja classificada de acordo com uma lista de tags pré-determinadas como positivo, negativo, neutro.
Hoje, você encontra na Internet esses modelos de linguagem natural no idioma de sua escolha (por exemplo, no model hub da Hugging Face), bem como o código para ajustá-los à tarefa de classificação de texto.
Então qual é o problema?
Problema: conjunto de dados desbalanceado
Na realidade, quando uma pessoa de uma empresa quer treinar um modelo de classificação de texto, muitas vezes se depara com 2 problemas:
- falta de dados
- desequilíbrio de dados por categorias
Voltarei em um post futuro a uma solução para o problema n°1. Em relação ao problema n°2, gostaria de compartilhar aqui um código que deve ser usado sempre.
Esse código modifica a função de perda (loss function) usada ao ajustar um modelo de linguagem na tarefa de classificação de texto que, sem esse código, dá um peso igual aos dados no cálculo de perda que permite atualizar os valores dos parâmetros do modelo durante o treinamento. A consequência disso é que a função de perda original favorece as categorias majoritárias. Assim, o modelo não será muito eficiente na produção com os dados que devem ser classificados nas categorias minoritárias do conjunto de dados de treinamento.
Nota: a literatura também propõe outras soluções, como duplicar dados de categorias minoritárias para obter um conjunto de dados balanceado, mas o risco de fazer isso é especializar o modelo para reconhecer um tipo de dado específico. Isso, portanto, não resolve realmente nosso problema para o uso do modelo em produção.
Solução: função de perda ponderada
Sendo o notebook text_classification.ipynb da Hugging Face a referência quando se trata de ajustar um modelo de linguagem à tarefa de classificação de texto, proponho abaixo um código que modifica esse notebook ao nível da criação do Trainer.
Esse código permite:
- calcular o vetor de peso
- criar a nova classe de Trainer com a função de perda ponderada
- instanciar um Trainer com essa nova classe
import torch
from torch import nn
# the model is waiting for "labels", not "label"
encoded_dataset = encoded_dataset.rename_column("label", "labels")
# Get training encoded data into DataFrame
df = encoded_dataset["train"].to_pandas()
# Get a vector of weights (lowest weight for majority class)
class_weights = (1 - (df["labels"].value_counts().sort_index() / len(df))).values
# Put the weights vector to cuda as a Pytorch tensor
class_weights = torch.from_numpy(class_weights).float().to("cuda")
# Create a Trainer with the weighted loss function
class WeightedLossTrainer(Trainer):
def compute_loss(self, model, inputs, return_outputs=False):
# Feed inputs to model and extract logits
outputs = model(**inputs)
logits = outputs.get("logits")
# Extract labels
labels = inputs.get("labels")
# define loss function with calss weights
loss_func = nn.CrossEntropyLoss(weight=class_weights)
# Compute loss
loss = loss_func(logits, labels)
return (loss, outputs) if return_outputs else loss
# Use WeightedLossTrainer instead of Trainer
from transformers import Trainer
validation_key = "validation_mismatched" if task == "mnli-mm" else "validation_matched" if task == "mnli" else "validation"
trainer = WeightedLossTrainer(
model,
args,
train_dataset=encoded_dataset["train"],
eval_dataset=encoded_dataset[validation_key],
tokenizer=tokenizer,
compute_metrics=compute_metrics
)
Agora você só precisa copiar/colar esse código (ou usar o notebook Text_Classification_on_GLUE_with_weighted_Loss.ipynb diretamente) com seus próprios dados. Et voilà.
Fonte
Para ter todos os detalhes sobre essa função de perda ponderada e seu uso na biblioteca Transformers da Hugging Face, convido você a consultar este vídeo que é a fonte desse artigo.
Sobre o autor: Pierre Guillou é consultor de IA no Brasil e na França. Entre em contato com ele por meio de seu perfil no LinkedIn.