Inferência de modelos usando Hugging Face Transformers para NLP
- Essa documentação foi descontinuada e pode não estar atualizada. O produto, serviço ou tecnologia mencionados neste conteúdo não são mais suportados.
- Databricks recomenda o uso de
ai_query
para a inferência de lotes. Consulte Realizar inferência de lotes LLM usando AI Functions.
Este artigo mostra como usar o site Hugging Face Transformers para inferência de modelos de processamento de linguagem natural (NLP).
Hugging Face transformers fornece a classe de pipeline para usar o modelo pré-treinado para inferência. O pipeline de transformadores suporta uma ampla gama de tarefas de PNL que o senhor pode usar facilmente em Databricks.
Requisitos
- MLflow 2.3
- Quaisquer clusters com a biblioteca Hugging Face
transformers
instalada podem ser usados para inferência de lotes. A bibliotecatransformers
vem pré-instalada no Databricks Runtime 10.4 LTS MLe acima. Muitos dos modelos populares de NLP funcionam melhor em hardware de GPU, portanto, você pode obter o melhor desempenho usando hardware de GPU recente, a menos que use um modelo especificamente otimizado para uso em CPUs.
Use os UDFs do Pandas para distribuir o cálculo do modelo em um clustering do Spark
Ao fazer experimentos com modelos pré-treinados, o senhor pode usar os UDFs doPandas para envolver o modelo e realizar cálculos em CPUs ou GPUs do worker. Pandas Os UDFs distribuem o modelo para cada worker.
O senhor também pode criar um Hugging Face Transformers pipeline para tradução automática e usar um Pandas UDF para executar o pipeline no worker de um Spark clustering:
import pandas as pd
from transformers import pipeline
import torch
from pyspark.sql.functions import pandas_udf
device = 0 if torch.cuda.is_available() else -1
translation_pipeline = pipeline(task="translation_en_to_fr", model="t5-base", device=device)
@pandas_udf('string')
def translation_udf(texts: pd.Series) -> pd.Series:
translations = [result['translation_text'] for result in translation_pipeline(texts.to_list(), batch_size=1)]
return pd.Series(translations)
Definir o device
dessa maneira garante que as GPUs sejam usadas se estiverem disponíveis em
o clustering.
O pipeline Hugging Face para tradução retorna uma lista de objetos Python dict
, cada um com um único key translation_text
e um valor contendo o texto traduzido. Esse UDF extrai a tradução dos resultados para retornar uma série do Pandas com apenas o texto traduzido. Se o seu pipeline foi construído para usar GPUs com a configuração device=0
, então o Spark reatribui automaticamente as GPUs nos nós do worker se o seu clustering tiver instâncias com várias GPUs.
Para usar o UDF para traduzir uma coluna de texto, o senhor pode chamar o UDF em uma instrução select
:
texts = ["Hugging Face is a French company based in New York City.", "Databricks is based in San Francisco."]
df = spark.createDataFrame(pd.DataFrame(texts, columns=["texts"]))
display(df.select(df.texts, translation_udf(df.texts).alias('translation')))
Devolver tipos de resultados complexos
Usando as UDFs do Pandas, o senhor também pode retornar uma saída mais estruturada. Por exemplo, no reconhecimento de entidades nomeadas, o pipeline retorna uma lista de objetos dict
contendo a entidade, sua extensão, tipo e uma pontuação associada. Embora semelhante ao exemplo da tradução, o tipo de retorno para a anotação @pandas_udf
é mais complexo no caso do reconhecimento de entidades nomeadas.
O senhor pode ter uma noção dos tipos de retorno a serem usados por meio da inspeção dos resultados do pipeline, por exemplo, executando o pipeline no driver.
Neste exemplo, use o código a seguir:
from transformers import pipeline
import torch
device = 0 if torch.cuda.is_available() else -1
ner_pipeline = pipeline(task="ner", model="Davlan/bert-base-multilingual-cased-ner-hrl", aggregation_strategy="simple", device=device)
ner_pipeline(texts)
Para produzir a anotação:
[[{'entity_group': 'ORG',
'score': 0.99933606,
'word': 'Hugging Face',
'start': 0,
'end': 12},
{'entity_group': 'LOC',
'score': 0.99967843,
'word': 'New York City',
'start': 42,
'end': 55}],
[{'entity_group': 'ORG',
'score': 0.9996372,
'word': 'Databricks',
'start': 0,
'end': 10},
{'entity_group': 'LOC',
'score': 0.999588,
'word': 'San Francisco',
'start': 23,
'end': 36}]]
Para representar isso como um tipo de retorno, você pode usar um array
dos campos struct
, listando as entradas dict
como os campos do struct
:
import pandas as pd
from pyspark.sql.functions import pandas_udf
@pandas_udf('array<struct<word string, entity_group string, score float, start integer, end integer>>')
def ner_udf(texts: pd.Series) -> pd.Series:
return pd.Series(ner_pipeline(texts.to_list(), batch_size=1))
display(df.select(df.texts, ner_udf(df.texts).alias('entities')))
Ajuste de desempenho
Há vários key aspectos para ajustar o desempenho do UDF. A primeira é usar cada GPU de forma eficaz, o que o senhor pode ajustar alterando o tamanho dos lotes enviados à GPU pelo pipeline Transformers. A segunda é garantir que o site DataFrame esteja bem particionado para utilizar todo o clustering.
Por fim, o senhor pode desejar armazenar em cache o modelo Hugging Face para economizar tempo de carregamento do modelo ou custos de entrada.
Escolha um tamanho de lote
Embora os UDFs descritos acima devam funcionar imediatamente com um batch_size
de 1, isso pode não usar o recurso disponível para o trabalhador de forma eficiente. Para melhorar o desempenho, ajuste o tamanho dos lotes de acordo com o modelo e o hardware no clustering. Databricks recomenda experimentar vários tamanhos de lotes para o pipeline em seu clustering para encontrar o melhor desempenho. Leia mais sobre pipeline lotes e outras opções de desempenho na documentação Hugging Face.
Tente encontrar um tamanho de lote que seja grande o suficiente para que ele impulsione a utilização total da GPU, mas que não resulte em erros no site CUDA out of memory
. Quando o senhor recebe erros no site CUDA out of memory
durante o ajuste, é necessário desconectar e reconectar o Notebook para liberar a memória usada pelo modelo e os dados na GPU.
Monitore o desempenho da GPU visualizando as métricas de clustering ao vivo para um clustering e escolhendo uma métrica, como gpu0-util
para utilização do processador da GPU ou gpu0_mem_util
para utilização da memória da GPU.
Ajuste o paralelismo com programação em nível de estágio
Por default, Spark programar uma tarefa por GPU em cada máquina. Para aumentar o paralelismo, o senhor pode usar a programação em nível de estágio para informar ao Spark quantas tarefas devem ser executadas por GPU. Por exemplo, se o senhor quiser que o Spark execute duas tarefas por GPU, poderá especificar isso da seguinte forma:
from pyspark.resource import TaskResourceRequests, ResourceProfileBuilder
task_requests = TaskResourceRequests().resource("gpu", 0.5)
builder = ResourceProfileBuilder()
resource_profile = builder.require(task_requests).build
rdd = df.withColumn('predictions', loaded_model(struct(*map(col, df.columns)))).rdd.withResources(resource_profile)
Reparticionar dados para usar todo o hardware disponível
A segunda consideração sobre o desempenho é fazer uso total do hardware em seu clustering. Em geral, um pequeno múltiplo do número de GPUs em seu worker (para clustering de GPU) ou do número de núcleos em todo o worker em seu clustering (para clustering de CPU) funciona bem. Sua entrada DataFrame pode já ter partições suficientes para aproveitar o paralelismo do clustering. Para ver quantas partições o DataFrame contém, use df.rdd.getNumPartitions()
. O senhor pode reparticionar um DataFrame usando repartitioned_df = df.repartition(desired_partition_count)
.
Armazenar o modelo em cache no DBFS ou em pontos de montagem
Se estiver carregando com frequência um modelo de um cluster diferente ou reiniciado, o senhor também pode desejar armazenar em cache o modelo Hugging Face no volumeDBFS root ou em um ponto de montagem. Isso pode diminuir os custos de entrada e reduzir o tempo de carregamento do modelo em um cluster novo ou reiniciado. Para fazer isso, defina a variável de ambiente TRANSFORMERS_CACHE
em seu código antes de carregar o pipeline.
Por exemplo:
import os
os.environ['TRANSFORMERS_CACHE'] = '/dbfs/hugging_face_transformers_cache/'
Como alternativa, o senhor pode obter resultados semelhantes registrando o modelo no MLflow com o flavor MLflow transformers
.
Notebook: Hugging Face Inferência de transformadores e MLflow logging
Para começar rapidamente com o código de exemplo, este Notebook é um exemplo completo de resumo de texto usando a inferência de pipeline do Hugging Face Transformers e o registro MLflow.
Hugging Face Caderno de inferência do pipeline de Transformers
Recurso adicional
O senhor pode fazer o ajuste fino do seu modelo Hugging Face com o seguinte guia:
- Preparar dados para o ajuste fino dos modelos Hugging Face
- Ajuste fino dos modelos Hugging Face para uma única GPU
Saiba mais sobre o que são os Hugging Face Transformers?