Wnioskowanie modelu przy użyciu transformatorów twarzy przytulania dla nlp

W tym artykule pokazano, jak używać funkcji przekształcania twarzy Hugging do wnioskowania modelu przetwarzania języka naturalnego (NLP).

Przytulanie transformatorów twarzy udostępnia klasę potoków do używania wstępnie wytrenowanego modelu do wnioskowania. 🤗 Potoki transformatorów obsługują szeroką gamę zadań NLP, których można łatwo używać w usłudze Azure Databricks.

Wymagania

  • MLflow 2.3
  • Dowolny klaster z zainstalowaną biblioteką rozpoznawania twarzy transformers hugging może służyć do wnioskowania wsadowego. Biblioteka transformers jest wstępnie zainstalowana w środowisku Databricks Runtime 10.4 LTS ML lub nowszym. Wiele popularnych modeli NLP działa najlepiej na sprzęcie gpu, więc możesz uzyskać najlepszą wydajność przy użyciu ostatniego sprzętu procesora GPU, chyba że używasz modelu specjalnie zoptymalizowanego do użycia na procesorach CPU.

Dystrybuowanie obliczeń modelu w klastrze Spark przy użyciu funkcji zdefiniowanej przez użytkownika biblioteki Pandas

Podczas eksperymentowania ze wstępnie wytrenowanym modelem można użyć funkcji zdefiniowanych przez użytkownika biblioteki Pandas do opakowania modelu i wykonywania obliczeń na procesorach roboczych lub procesorach GPU. Funkcje zdefiniowane przez użytkownika biblioteki Pandas dystrybuują model do każdego procesu roboczego.

Możesz również utworzyć potok Hugging Face Transformers na potrzeby tłumaczenia maszynowego i użyć funkcji zdefiniowanej przez użytkownika biblioteki Pandas do uruchomienia potoku w ramach procesów roboczych klastra Spark:

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)

Ustawienie właściwości device w ten sposób gwarantuje, że procesory GPU są używane, jeśli są dostępne w klastrze.

Potoki przytulania twarzy do tłumaczenia zwracają listę obiektów języka Python dict , z których każdy ma jeden klucz translation_text i wartość zawierającą przetłumaczony tekst. Ta funkcja UDF wyodrębnia tłumaczenie z wyników, aby zwrócić serię biblioteki Pandas tylko z przetłumaczonym tekstem. Jeśli potok został skonstruowany tak, aby używał procesorów GPU przez ustawienie device=0, platforma Spark automatycznie ponownie przypisuje procesory GPU w węzłach roboczych, jeśli klaster ma wystąpienia z wieloma procesorami GPU.

Aby przetłumaczyć kolumnę tekstową przy użyciu funkcji zdefiniowanej przez użytkownika, możesz wywołać funkcję UDF w instrukcji 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')))

Zwracanie złożonych typów wyników

Korzystając z funkcji zdefiniowanych przez użytkownika biblioteki Pandas, można również zwrócić bardziej ustrukturyzowane dane wyjściowe. Na przykład w funkcji rozpoznawania jednostek nazwanych potoki zwracają listę dict obiektów zawierających jednostkę, jej zakres, typ i skojarzony wynik. Podobnie jak w przypadku tłumaczenia, zwracany typ @pandas_udf adnotacji jest bardziej złożony w przypadku rozpoznawania nazwanych jednostek.

Możesz uzyskać informacje o typach zwracanych, które mają być używane przez inspekcję wyników potoku, na przykład przez uruchomienie potoku w sterowniku.

W tym przykładzie użyj następującego kodu:

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)

Aby uzyskać adnotacje:

[[{'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}]]

Aby przedstawić to jako typ zwracany, możesz użyć arraystruct pola z listą dict wpisów jako pól elementu 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')))

Dostrajanie wydajności

Istnieje kilka kluczowych aspektów dostrajania wydajności funkcji zdefiniowanej przez użytkownika. Pierwszym z nich jest efektywne użycie każdego procesora GPU, które można dostosować, zmieniając rozmiar partii wysyłanych do procesora GPU przez potok Transformers. Drugim jest upewnienie się, że ramka danych jest dobrze podzielona na partycje, aby korzystać z całego klastra.

Na koniec możesz buforować model hugging Face, aby zaoszczędzić czas ładowania modelu lub koszty ruchu przychodzącego.

Wybieranie rozmiaru partii

Chociaż opisane powyżej funkcje zdefiniowane przez użytkownika powinny działać poza ramką z wartością batch_size 1, może to nie korzystać z zasobów dostępnych dla procesów roboczych. Aby zwiększyć wydajność, dostosuj rozmiar partii do modelu i sprzętu w klastrze. Usługa Databricks zaleca wypróbowanie różnych rozmiarów partii dla potoku w klastrze, aby znaleźć najlepszą wydajność. Przeczytaj więcej na temat przetwarzania wsadowego potoku i innych opcji wydajności w dokumentacji hugging Face.

Spróbuj znaleźć rozmiar partii, który jest wystarczająco duży, aby był oparty na pełnym wykorzystaniu procesora GPU, ale nie powoduje CUDA out of memory błędów. W przypadku wystąpienia CUDA out of memory błędów podczas dostrajania należy odłączyć i ponownie dołączyć notes, aby zwolnić pamięć używaną przez model i dane w procesorze GPU.

Monitoruj wydajność procesora GPU, wyświetlając metryki klastra na żywo dla klastra i wybierając metrykę, taką jak gpu0-util użycie procesora GPU lub gpu0_mem_util użycie pamięci procesora GPU.

Dostrajanie równoległości przy użyciu planowania na poziomie etapu

Domyślnie platforma Spark planuje jedno zadanie na procesor GPU na każdej maszynie. Aby zwiększyć równoległość, można użyć planowania na poziomie etapu, aby poinformować platformę Spark, ile zadań do uruchomienia na procesor GPU. Jeśli na przykład chcesz, aby platforma Spark uruchamiała dwa zadania na procesor GPU, możesz to określić w następujący sposób:

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)

Ponowne partycjonowania danych do korzystania ze wszystkich dostępnych sprzętów

Druga kwestia wydajności polega na pełnym wykorzystaniu sprzętu w klastrze. Ogólnie rzecz biorąc, niewielka liczba procesorów GPU dla procesów roboczych (w przypadku klastrów gpu) lub liczba rdzeni w klastrze (w przypadku klastrów procesora CPU) działa dobrze. Ramka danych wejściowych może już mieć wystarczającą liczbę partycji, aby skorzystać z równoległości klastra. Aby zobaczyć, ile partycji zawiera ramka danych, użyj polecenia df.rdd.getNumPartitions(). Ramki danych można ponownie partycjonować przy użyciu polecenia repartitioned_df = df.repartition(desired_partition_count).

Buforowanie modelu w systemie DBFS lub w punktach instalacji

Jeśli często ładujesz model z różnych lub ponownie uruchomionych klastrów, możesz również buforować model Hugging Face w woluminie głównym systemu DBFS lub w punkcie instalacji. Może to zmniejszyć koszty ruchu przychodzącego i skrócić czas ładowania modelu w nowym lub ponownie uruchomionym klastrze. W tym celu ustaw zmienną TRANSFORMERS_CACHE środowiskową w kodzie przed załadowaniem potoku.

Na przykład:

import os
os.environ['TRANSFORMERS_CACHE'] = '/dbfs/hugging_face_transformers_cache/'

Alternatywnie możesz osiągnąć podobne wyniki, rejestrując model do biblioteki MLflow z smakiem MLflowtransformers.

Notes: przytulanie wnioskowania funkcji przekształcania twarzy i rejestrowania MLflow

Aby szybko rozpocząć pracę z przykładowym kodem, ten notes jest przykładem kompleksowego podsumowania tekstu przy użyciu potoków hugging Face Transformers wnioskowania i rejestrowania MLflow.

Przytulanie potoków przekształcania twarzy do wnioskowania notesu

Pobierz notes

Dodatkowe zasoby

Model hugging Face można dostosować, wykonując następujące przewodniki:

Dowiedz się więcej o tym, co to są transformatory twarzy?