NLP nas empresas | Adapter-Transformers Para Dummies

Pierre Guillou
10 min readSep 1, 2021

--

A biblioteca Adapter-Transformers para Dummies
A biblioteca Adapter-Transformers para Dummies

Este post é sobre a apresentação da biblioteca adapter-transformers ministrada por Pierre Guillou durante a conferência do dia 01/09/2021 do grupo Deep Learning Brasil NLP (CEIA: Centro de Excelência em Inteligência Artificial). Além de detalhar as suas funcionalidades, destaca seu interesse em solucionar problemas com modelos de NLP na produção, fornece os principais códigos e dá links para casos práticos com notebooks (modeling of language models, NER, QA, multi-tasks…).

Sumário

  • Problema(s) da IA em produção
    1. A cada processo (unitário), um modelo de IA
    2. Alguns problemas da abordagem clássica no uso dos modelos de IA na produção
  • Um adapter por processo NLP
  • A biblioteca Adapter-Transformers
  • Visão geral
    1. Usando um adapter pré-treinado para inferência
    2. Tipos de adapter
    3. Arquitetura de um adapter
    4. Composição de adapters
    5. Treinamento de um adapter
  • Integração com o Model Hub da Hugging Face
  • 5 tutoriais (com notebooks) para praticar (em português!) a biblioteca adapter-transformers
  • Referências

Problema(s) da IA em produção

1. A cada processo (unitário), um modelo de IA

Uma empresa tem muitas atividades e cada uma corresponde a um fluxo de processos unitários de NLP (fonte da imagem: The Main Approaches to Natural Language Processing Tasks)
Uma empresa tem muitas atividades e cada uma corresponde a um fluxo de processos unitários de NLP (fonte da imagem: The Main Approaches to Natural Language Processing Tasks)

A empresa que chamaremos BRASIOJE vende produtos de bem-estar por meio de seu site. Ela usa as mídias sociais para detectar tendências, promover sua marca e realizar marketing direcionado.

BRASIOJE precisa de IA para automatizar tarefas repetitivas e cotidianas das suas atividades, como as seguintes:

  • (NER — classificação de tokens) Em um mundo com muita competição, ele precisa comparar seus produtos com os de seus concorrentes. Portanto, deve automatizar a extração de informações (EN: Entidades Nomeadas) como PRODUTO, VALOR, DATA, etc. nos textos publicados nos sites deles.
  • (textos — classificação de textos) Através dos seus diversos canais online, o seu serviço de atendimento ao cliente recebe mensagens de todo o tipo (pedido de compra, reclamação, etc.) que deve poder classificar automaticamente para encaminhá-las para os respectivos departamentos.
  • (QA — Questões-Respostas) Ela deve encontrar automaticamente as respostas para novas perguntas de seus clientes em textos já publicados antes de escrever novas respostas por alguém de sua equipe (assim, ela poderá atualizar frequentemente no seu site sua lista de Perguntas Frequentes que alimenta também seu chatbot).

Essa lista não esgota as necessidades da BRASIOJE em termos de modelos de NLP para automatizar (parte de) suas atividades. Essa é a realidade de qualquer negócio (aconselhamos os interessados ​​nesse tipo de análise a seguir o curso de Andrew NG: IA para todos).

2. Alguns problemas da abordagem clássica no uso dos modelos de IA na produção

O método clássico para obter modelos de NLP treinados para resolver as tarefas listadas acima é o seguinte:

  • Download de um modelo de linguagem natural do tipo BERT que entende a linguá dos textos utilizados pela empresa. Por exemplo, se for o português do Brasil, o BRASIOJE poderia baixar o BERTimbau grande (340 milhões de parâmetros, 1.3 GB).
  • (opção) Ajuste fino (finetuning) desse modelo com um corpus de textos utilizados nas atividades da empresa, de forma a especializar o modelo de linguagem natural para o domínio linguístico particular desses textos.
  • Ajuste fino (finetuning) desse último modelo de linguagem natural para cada uma das tarefas de NLP (Transfer Learning) necessárias para automatizar as atividades da empresa, o que leva à criação de um modelo por tarefa de NLP (especialização pelo finetuning de todas as camadas do modelo e da sua task head).

As consequências dessa abordagem clássica são as seguintes: um modelo inteiro para ajustar (que hiper-parâmetros?), para ser armazenado/acesso e mantido (que tamanho total?), e tudo isso por processo unitário de uma atividade (quantos processos unitários?).

Se assumirmos que esse modelo é baseado em um BERTimbau grande (pode ser um modelo bem maior…), isso significa cerca de 340 milhões de parâmetros a serem mantidos por unitário de atividade para um peso de 1,3 GB por modelo…

Quando voltamos à lista de atividades (e, portanto, processos unitários/modelos) de uma empresa, esses números exigem que se encontre uma solução diferente daquela da abordagem clássica para usar modelos de NLP na produção.

Um adapter por processo NLP

Abordagem clássica vs abordagem pelos adapters
Abordagem clássica vs abordagem pelos adapters

Os poucos problemas listados acima não são os únicos sobre o uso de modelos de NLP na produção, mas são recorrentes (podemos citar também os riscos de Catastrophic Forgetting).

Existem soluções diferentes desde o modelo destilado (DistilBERT) ou MiniLM cujo peso é consideravelmente reduzido para um desempenho equivalente, a modelos multitarefas como o T5 que dispensa o armazenamento de vários modelos, as APIs para acesso a modelos que aceleram a sua utilização na produção (Accelerated Inference API).

Há também uma biblioteca recente (2020) baseada em estratégias de pesquisa publicadas (veja os artigos nas Referências) chamada adapter-transformers.

A biblioteca Adapter-Transformers

A biblioteca adapter-transformers tem:

Ela é baseada na implementação Pytorch da biblioteca de NLP a mais usada (transformers da Hugging Face) e fornece uma solução elegante e prática para esse problema da multiplicidade de modelos ajustados para serem mantidos por uma empresa.

Aqui estão as principais ideias:

  • o método clássico de ajustar um modelo geral de tipo BERT para uma tarefa de NLP a partir de um novo banco de dados corre o risco de destruir o aprendizado presente em embeddings e parâmetros das camadas do modelo, atualizando seus valores (se chama Catastrophic Forgetting);
  • mantendo os valores de seus parâmetros intactos e treinando apenas poucos novos parâmetros no nível de cada camada do modelo de linguagem (cujo número total é de aproximadamente 1% daquele do modelo geral), é possível alcançar um desempenho equivalente ao obtido pelo método clássico (um pouco como se complementássemos o aprendizado geral por um aprendizado particular);
  • agrupando esses novos parâmetros em um módulo chamado adapter, que pode ser carregado no modelo geral por uma única linha de código, é suficiente armazenar apenas um modelo geral de NLP (BERT, RoBERTa, XLM-RoBERTa, DistilBERT, BART, mBART, GPT-2) no servidor de produção e não um por banco de dados.

Visão geral

A biblioteca adapter-transformers é um fork da biblioteca transformers da Hugging Face. Isso significa 2 coisas:

  1. Cada atualização da biblioteca transformers é seguida por uma da biblioteca adapter-transformers (mas pode levar algum tempo entre os dois).
  2. O código a ser usado nos notebooks e scripts vem da biblioteca transformers (python e Pytorch)! A biblioteca adapter-transformers adicionou apenas algumas classes e funções, as principais das quais são mostradas abaixo.

Nota: para aprender como fazer um finetuning de um modelo já treinado com a biblioteca transformers da Hugging Face, leia a página “Fine-tuning a pretrained model”.

1. Usando um adapter pré-treinado para inferência

from transformers import BertTokenizer, BertForSequenceClassification

# load pre-trained BERT from Huggingface
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
# load pre-trained task adapter + head from Adapter Hub
adapter_name = model.load_adapter('sst-2@ukp')

# activate the adapter we just loaded, so that it is used in every forward pass
model.set_active_adapters(adapter_name)

2. Tipos de adapter

  • Lang adapter (adapter de idioma): os adapters de idioma são usados para aprender transformações específicas do idioma ao nível dos embeddings (invertible adapters) além do nível dos blocos do transformer.
  • Task adapter (adaptador de tarefa): os adapters de tarefa são ajustados para aprender representações para tarefas downstream específicas, como análise de sentimento, resposta a perguntas, etc. ao nível dos blocos do transformer.

3. Arquitetura de um adapter

O interior de um adapter é uma projeção simples para baixo e para cima (semelhante a um auto-encoder) combinada com uma conexão residual.

adapter-transformers têm algumas arquiteturas comuns integradas:

Adapter Pfeiffer à direita (fonte: AdapterFusion: Non-Destructive Task Composition for Transfer Learning)
Adapter Pfeiffer à direita (fonte: AdapterFusion: Non-Destructive Task Composition for Transfer Learning)

4. Composição de adapters

Stack: a composição Stack pode ser usada para empilhar vários adapters uns sobre os outros (lang >> task adapters por exemplo).

import transformers.adapters.composition as ac

// ...

model.add_adapter("a")
model.add_adapter("b")
model.add_adapter("c")

model.active_adapters = ac.Stack("a", "b", "c")

Fuse: a composição Fuse pode ser usada para ativar uma camada de fusão de adapters. AdapterFusion é uma maneira não destrutiva de combinar o conhecimento de vários adapters pré-treinados em uma nova tarefa downstream.

import transformers.adapters.composition as ac

// ...

model.add_adapter("d")
model.add_adapter("e")
model.add_adapter("f")
model.add_adapter_fusion(["d", "e", "f"])

model.active_adapters = ac.Fuse("d", "e", "f")

Parallel: a composição Parallel pode ser usada para habilitar a inferência multitarefa paralela em diferentes adapters, cada um com sua própria head de predição.

model = AutoModelWithHeads.from_pretrained("distilbert-base-uncased")
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")

adapter1 = model.load_adapter("sts/sts-b@ukp")
adapter2 = model.load_adapter("sts/mrpc@ukp")

model.active_adapters = ac.Parallel(adapter1, adapter2)

input_ids = tokenizer("Adapters are great!", "Adapters are awesome!", return_tensors="pt")

output1, output2 = model(**input_ids)

print("STS-B adapter output:", output1[0].item())
print("MRPC adapter output:", bool(torch.argmax(output2[0]).item()))

5. Treinamento de um adapter

O treinamento de um adapter em um dataset requer apenas pequenas modificações do código de treinamento do modelo completo (cujos valores de seus parâmetros não vão ser alterados) em comparação do de treinamento da Hugging Face (veja o notebook “Training an Adapter for a Transformer model”).

  1. Preparação do dataset: é a biblioteca transformers que vai ser usada (lei “Preparing the datasets” da Hugging Face).
  2. Carregue o modelo transformer e crie uma task head com a classe AutoModelHeads (é possível também carregar um modelo com as classes da Hugging Face como as Auto Classes: AutoTokenizer, AutoModelForSequenceClassification, etc.):
from transformers import AutoTokenizer, AutoModelWithHeads

# load pre-trained transformer model from Hugging Face
tokenizer = AutoTokenizer.from_pretrained(model_args.model_name_or_path)
model = AutoModelWithHeads.from_pretrained(
model_args.model_name_or_path,
config=config,
)
# add a Classification head to the pre-trained model
model.add_classification_head(data_args.task_name, num_labels=num_labels)

3. Em comparação com o ajuste fino do modelo completo, há apenas uma adaptação significativa que precisamos fazer: adicionar um novo adapter (com a sua configuração), ativá-lo e congelar todos os outros parâmetros.

# task adapter - only add if not existing
if task_name not in model.config.adapters:
# resolve the adapter config
adapter_config = AdapterConfig.load(
adapter_args.adapter_config,
non_linearity=adapter_args.adapter_non_linearity,
reduction_factor=adapter_args.adapter_reduction_factor,
)
# add a new adapter
model.add_adapter(
task_name,
config=adapter_config
)
# Enable adapter training and freeze all parameters but adapter ones
model.train_adapter(task_name)

Nota: a função model.train_adapter(task_name) não vai lançar o treinamento do modelo com adapter. Ela vai apenas congelar todos os parâmetros menos os do adapter adicionado.

4. Treinamento do modelo com adapter: é o código padrão da Hugging face para ser usado.

from transformers import TrainingArguments, Trainertrainer = Trainer(
model=model,
args=TrainingArguments(...),
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
tokenizer=tokenizer,
(...)
)
trainer.train()

5. Arquivamento do adapter treinado (e da task head também).

model.save_adapter(path_to_adapter_folder, adapter_name)
model.save_head(path_to_adapter_folder, head_name)

Et voilà! ;-)

Nota: leia também as 2 dicas seguintes:

Dica 1

Adapter weights are usually initialized randomly. That is why we require a higher learning rate. We have found that a default adapter learning rate of 1e-4 works well for most settings.

Dica 2

Depending on your data set size you might also need to train longer than usual. To avoid overfitting you can evaluating the adapters after each epoch on the development set and only save the best model.

Integração com o Model Hub da Hugging Face

Começando com a v2.1 de adapter-transformers, pode baixar adapters e carregá-los para o Model Hub do HuggingFace.

Este documento descreve como interagir com o Model Hub ao trabalhar com adapters.

Aqui está a lista dos adapters no Model Hub da Hugging Face: https://huggingface.co/models?filter=adapter-transformers.

5 tutoriais (com notebooks) para praticar (em português!) a biblioteca adapter-transformers

Uma solução para colocar os modelos BERT em produção com Adapters para transformers

Como ajustar um modelo de linguagem natural como BERT a um novo domínio linguístico com um Adapter?

Como ajustar um modelo de linguagem natural como BERT para a tarefa de classificação de tokens (NER) com um Adapter?

Como ajustar um modelo de linguagem natural como BERT para a tarefa de Question-Answering (QA) com um Adapter?

Como criar um modelo BERT de Question-Answering (QA) de desempenho aprimorado com AdapterFusion?

Referências

Sobre o autor: Pierre Guillou é Head of AI na startup de HealthTech Laudite (Brasil), e consultor de IA no Brasil e na França. Entre em contato com ele por meio de seu perfil no LinkedIn.

--

--

Pierre Guillou

AI, Generative AI, Deep learning, NLP models author | Europe (Paris, Bruxelles, Liège) & Brazil