IA & empresas | Diminua o tempo de inferência de modelos Transformer com BetterTransformer

Pierre Guillou
7 min readNov 22, 2022
Crédito: BetterTransformer, Out of the Box Performance for Hugging Face Transformers
Crédito: BetterTransformer, Out of the Box Performance for Hugging Face Transformers

Como pode obter inferências mais rapidamente (e de uma forma muito simples!) com seus modelos Transformer existentes (ou seja, sem precisar treiná-los novamente) e, assim, reduzir os custos do servidor de inferência? A solução vem através do código: BetterTransformer da Hugging Face! Este artigo apresenta esse grande avanço para todas as empresas que desejam usar seus modelos Transformer em produção com links para um notebook e um aplicativo para testar o BetterTransformer.

Problema: tempo de inferência dos modelos Transformer

Um problema frequentemente mencionado sobre os modelos Transformer quando queremos utilizá-los em produção é o tempo de inferência deles.

Não só um longo tempo (digamos mais de um segundo) é inconveniente para o usuário acostumado a ter os resultados de sua pesquisa no Google quase instantaneamente, mas também esse tempo de inferência tem um custo para as empresas (o custo do tempo de uso dos servidores de inferência) que, portanto, procuram reduzi-lo sem perder desempenho, ou seja, precisão das inferências.

Existem já soluções na Internet mas geralmente elas complicam o código de inferência e muitas vezes requer uma arquitetura otimizada ao nível do servidor de inferência, o que significa na prática que a maioria das pessoas e empresas não as utilizam por falta de competências técnicas internas e recursos financeiros dedicado a essas soluções.

Solução: BetterTransformer pelo Facebook

Felizmente, a pesquisa e desenvolvimento Open Source, apoiado por grandes empresas digitais e universidades, publica regularmente inovações de código que resolvem um a um os problemas encontrados pelo maior número.

Em julho de 2022 (comunicado), a equipe de Michael Gschwind (Facebook AI) publicou o BetterTransformer, uma biblioteca Pytorch para acelerar a inferência com um modelo Transformer do tipo Encoder (BERT, por exemplo)!

Na prática: BetterTransformer pelo Hugging Face

Tudo o que restava era sua implementação na biblioteca Transformers do Hugging Face (certamente, a biblioteca de modelos Transformer mais usada no mundo)!

Isso é feito desde o dia 17 de novembro de 2022 (comunicado) com a versão 1.5.0 do Optimum, a biblioteca que fornece um conjunto de ferramentas para otimizar o desempenho dos modelos da biblioteca Transformers.

Como? Precisa só converter seu modelo Transformer através da função BetterTransformer.transform()! (veja a documentação Hugging Face de BetterTransformer()).

from optimum.bettertransformer import BetterTransformer
model = BetterTransformer.transform(model)

Aceleração esperada

(fonte) A aceleração base é muito sensível ao hardware que você está usando, para o hardware mais recente (NVIDIA A100, V100) a aceleração é muito mais significativa do que as GPUs mais antigas (NVIDIA T4, etc.). Observe também que um aumento de velocidade também é observado nas CPUs, o que torna o método em geral mais rápido do que a implementação nativa no hardware mais utilizado.

CPU

Para testar na CPU o BetterTransformer e com modelos base e large de tipo BERT, criamos um App na Hugging Face Spaces:

Aqui está uma comparação dos tempos de inferência com a questão e contexto seguintes:

question = "Quem foi Dom Pedro II?"
context = "Dom Pedro II foi o segundo e último monarca do Império do Brasil, reinando por mais de 58 anos."
Tempos de predição na CPU entre os modelos base e large BERT in português, sem e com o uso de BetterTransformer
Tempos de predição na CPU entre os modelos base e large BERT in português, sem e com o uso de BetterTransformer
{
"BERT large": {
"score": 0.4765968918800354,
"answer": "o segundo e último monarca do Império do Brasil",
"time (s)": 0.99
},
"BERT large (BetterTransformer)": {
"score": 0.4765968918800354,
"answer": "o segundo e último monarca do Império do Brasil",
"time (s)": 0.91
},
"BERT base": {
"score": 0.2049650251865387,
"answer": "segundo e último monarca do Império do Brasil",
"time (s)": 0.3
},
"BERT base (BetterTransformer)": {
"score": 0.20496530830860138,
"answer": "segundo e último monarca do Império do Brasil",
"time (s)": 0.29
}
}

Podemos ver que nessa arquitetura de CPU do Space da Hugging Face (2 vCPU, 16 GiB de RAM), BetterTransformer melhora um pouco os tempos de inferência (8% com o BERT large, e 3% com o BERT base). No entanto, precisa testar com sua própria arquitetura, que pode ser mais recente (por exemplo, no Google Colab, observamos uma melhoria de 20% na CPU com o BERT large).

GPU

Para testar na GPU o BetterTransformer e com modelos base e large de tipo BERT, criamos um notebook disponível na github: question_answering_portuguese_with_BetterTransformer.ipynb

Aqui está uma comparação dos tempos de inferência com os mesmos questão e contexto:

Tempos de predição na GPU entre os modelos base e large BERT in português, sem e com o uso de BetterTransformer
Tempos de predição na GPU entre os modelos base e large BERT in português, sem e com o uso de BetterTransformer
{
"BERT large": {
"score": 0.47659653425216675,
"answer": "o segundo e último monarca do Império do Brasil",
"time (s)": 2.95
},
"BERT large (BetterTransformer)": {
"score": 0.4765964150428772,
"answer": "o segundo e último monarca do Império do Brasil",
"time (s)": 0.05
},
"BERT base": {
"score": 0.20496521890163422,
"answer": "segundo e último monarca do Império do Brasil",
"time (s)": 0.01
},
"BERT base (BetterTransformer)": {
"score": 0.20496511459350586,
"answer": "segundo e último monarca do Império do Brasil",
"time (s)": 0.01
}
}

Nessa GPU do Google Colab (Nvidia T4), o impacto do BetterTransformer no tempo de inferência é enorme com o BERT large: passamos de 2.95s para 0.05s, seja uma melhoria de 98%!

Código para usar BetterTransformer

Vamos ver o que precisa mudar no seu código para fazer predições com um modelo de QA (Question-Answering) em português.

model_name = "pierreguillou/bert-base-cased-squad-v1.1-portuguese"

Uma coisa a não esquecer: precisa pelo menos as versões 1.5.0 da biblioteca Optimum, 4.24.0 da Transformers, e 1.13.0 da Pytorch se quiser usar BetterTransformer() da Hugging Face.

# Pytorch 
!pip3 install -q --upgrade torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116
# Transformers
!pip install transformers>=4.24.0
# Optimum
!pip install optimum>=1.5.0

Caso 1 | Pytorch

Se preferir usar o código Pytorch, o código até agora foi o seguinte (fonte):

# !pip3 install torch torchvision torchaudio
# !pip install transformers
import torch, transformers
from transformers import AutoTokenizer, AutoModelForQuestionAnswering

# device
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'

# tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(device)

# input data
question = "Quem foi Dom Pedro II?"
context = "Dom Pedro II foi o segundo e último monarca do Império do Brasil, reinando por mais de 58 anos."

# prediction
inputs = tokenizer(question, context, return_tensors="pt").to(device)
with torch.no_grad():
outputs = model(**inputs)

answer_start_index = outputs.start_logits.argmax()
answer_end_index = outputs.end_logits.argmax()

predict_answer_tokens = inputs.input_ids[0, answer_start_index : answer_end_index + 1]
tokenizer.decode(predict_answer_tokens)

# segundo e último monarca do Império do Brasil

Para usar BetterTransformer com Pytorch, precisa só converter o seu modelo com BetterTransformer.transform() !

from optimum.bettertransformer import BetterTransformer
model = BetterTransformer.transform(model)

Et voilà! :-) A partir de agora, usarei sempre o código seguinte para fazer inferências com Pytorch:

# !pip3 install -q --upgrade torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116
# !pip install transformers>=4.24.0
# !pip install optimum>=1.5.0
import torch, transformers
from transformers import AutoTokenizer, AutoModelForQuestionAnswering
from optimum.bettertransformer import BetterTransformer

# device
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'

# tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(device)
model = BetterTransformer.transform(model)

# input data
question = "Quem foi Dom Pedro II?"
context = "Dom Pedro II foi o segundo e último monarca do Império do Brasil, reinando por mais de 58 anos."

# prediction
inputs = tokenizer(question, context, return_tensors="pt").to(device)
with torch.no_grad():
outputs = model(**inputs)

answer_start_index = outputs.start_logits.argmax()
answer_end_index = outputs.end_logits.argmax()

predict_answer_tokens = inputs.input_ids[0, answer_start_index : answer_end_index + 1]
tokenizer.decode(predict_answer_tokens)

# segundo e último monarca do Império do Brasil

Caso 2 | pipeline()

Se quiser usar pipeline(), o código até agora foi o seguinte (fonte):

# !pip3 install torch torchvision torchaudio
# !pip install transformers
# !pip install optimum>=1.5.0
import torch, transformers
from transformers import pipeline

# device
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'

# instantiate the pipeline
pipe = pipeline(task="question-answering", model=model_name, device=device)

# input data
question = "Quem foi Dom Pedro II?"
context = "Dom Pedro II foi o segundo e último monarca do Império do Brasil, reinando por mais de 58 anos."

# prediction
pipe(question=question, context=context)

# {'score': 0.20496521890163422,
# 'start': 19,
# 'end': 64,
# 'answer': 'segundo e último monarca do Império do Brasil'}

Para usar BetterTransformer com pipeline(), precisa só atualizar a importação do pipeline() da biblioteca Optimum (e não da Transformers) e atualizar a instância do pipeline() com accelerator="bettertransformer".

from optimum.pipelines import pipeline

pipe = pipeline(task="question-answering", model=model_name, accelerator="bettertransformer", device=device)

Et voilà! :-) A partir de agora, usarei sempre o código seguinte para fazer inferências com pipeline():

# !pip3 install -q --upgrade torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116
# !pip install optimum>=1.5.0
import torch, optimum
from optimum.pipelines import pipeline

# device
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'

# instantiate the pipeline
pipe = pipeline(task="question-answering", model=model_name, accelerator="bettertransformer", device=device)

# input data
question = "Quem foi Dom Pedro II?"
context = "Dom Pedro II foi o segundo e último monarca do Império do Brasil, reinando por mais de 58 anos."

# prediction
pipe(question=question, context=context)

# {'score': 0.20496521890163422,
# 'start': 19,
# 'end': 64,
# 'answer': 'segundo e último monarca do Império do Brasil'}

Referências

video “Accelerate Transformer inference on GPU with Optimum and Better Transformer

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.

--

--