IA & empresas | Diminua o tempo de inferência de modelos Transformer com BetterTransformer
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."
{
"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:
{
"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
- Optimum v1.5.0 : https://github.com/huggingface/optimum/releases/tag/v1.5.0
- Optimum HuggingFace docs: https://huggingface.co/docs/optimum/index
- BetterTransformer Pytorch post: https://pytorch.org/blog/a-better-transformer-for-fast-transformer-encoder-inference/
- BetterTransformer HuggingFace docs: https://huggingface.co/docs/optimum/bettertransformer/overview
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.