Поделиться через


Инференция модели с использованием Transformers от Hugging Face для задач обработки естественного языка (NLP)

Внимание

  • Поддержка этой документации прекращена, и она возможно больше не будет обновляться. Продукты, службы или технологии, упомянутые в этом контенте, больше не поддерживаются.
  • Databricks рекомендует использовать ai_query для пакетного вывода. См. раздел Выполнение пакетного вывода LLM с помощью функций ИИ.

В этой статье показано, как использовать трансформеры компании Hugging Face для вывода моделей в обработке естественного языка (NLP).

Платформа Hugging Face предоставляет класс pipelines для использования предварительно обученной модели на этапе вывода. 🤗 Конвейеры преобразователей поддерживают широкий спектр задач NLP, которые можно легко использовать в Azure Databricks.

Требования

  • MLflow 2.3
  • Любой кластер с установленной библиотекой transformers Hugging Face можно использовать для пакетного прогнозирования. Библиотека transformers предустановлена в Databricks Runtime 10.4 LTS ML и выше. Многие популярные модели NLP лучше всего работают на оборудовании GPU, поэтому вы можете получить лучшую производительность с помощью недавнего оборудования GPU, если вы не используете модель специально оптимизированную для использования на ЦП.

Используйте pandas UDFs для распределения вычислений модели в кластере Spark.

При проведении экспериментов с предварительно обученными моделями можно использовать Pandas UDF для упаковки модели и выполнения вычислений на рабочих CPU или GPU. Функции Pandas UDFs распределяют модель на каждого рабочего узла.

Вы также можете создать конвейер Hugging Face Transformers для машинного перевода и использовать UDF Pandas для запуска конвейера на рабочих узлах кластера 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)

Настройка device таким образом обеспечивает использование графических процессоров, если они доступны в кластере.

Конвейеры для перевода от Hugging Face возвращают список объектов Python dict, каждый из которых содержит один ключ translation_text и значение с переведённым текстом. Этот UDF извлекает перевод из результатов, чтобы вернуть серию Pandas только с переведенным текстом. Если конвейер создан для использования графических процессоров, задав параметр device=0, Spark автоматически переназначает графические процессоры на рабочих узлах, если в кластере есть экземпляры с несколькими GPU.

Чтобы использовать UDF для перевода текстового столбца, можно вызвать UDF в инструкции 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')))

Возврат сложных типов результатов

Используя пользовательские функции Pandas (UDF), вы также можете возвращать более структурированные выходные данные. Например, в распознавании именованных сущностей конвейеры возвращают список dict объектов, содержащих сущность, его диапазон, тип и связанную оценку. Аналогично примеру перевода, возвращаемый тип заметки @pandas_udf является более сложным в случае распознавания именованных сущностей.

Вы можете получить представление о типах возвращаемых данных, используемых с помощью проверки результатов конвейера, например, запуская конвейер на драйвере.

В этом примере используйте следующий код:

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)

Чтобы получить аннотации, выполните следующие действия.

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

Чтобы представить это в качестве возвращаемого типа, вы можете использовать поля array и struct, перечислив dict записи в качестве полей 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')))

Настройка производительности

Существует несколько ключевых аспектов настройки производительности UDF. Первое — эффективно использовать каждый GPU, который можно настроить, изменив размер пакетов, отправленных на GPU конвейером Преобразователей. Во-вторых, убедитесь, что DataFrame хорошо разделен для использования всего кластера.

Наконец, вы можете кэшировать модель Hugging Face, чтобы сэкономить время загрузки модели или затраты на входящий трафик.

Выбор размера пакета

Хотя описанные выше UDF должны работать из коробки с batch_size 1, это может не обеспечивать эффективное использование доступных ресурсов. Чтобы повысить производительность, настройте размер пакета на модель и оборудование в кластере. Databricks рекомендует попробовать различные размеры пакетов для конвейера в кластере, чтобы найти оптимальную производительность. Узнайте больше о пакетной обработке потока и других параметрах производительности в документации Hugging Face.

Попробуйте найти размер пакета, который достаточно велик, чтобы он приводил к полному использованию GPU, но не приводит к CUDA out of memory ошибкам. При возникновении ошибок во время настройки необходимо отсоединить записную книжку и снова подключить её, чтобы освободить память, используемую моделью и данными в графическом процессоре (GPU).

Отслеживайте производительность GPU, просматривая живые метрики кластера и выбирая метрику, такую как gpu0-util для использования процессора GPU или gpu0_mem_util для использования памяти GPU.

Настройка параллелизма с планированием на уровне этапа

По умолчанию Spark планирует одну задачу на gpu на каждом компьютере. Чтобы увеличить параллелизм, можно использовать планирование на уровне этапа, чтобы сообщить Spark о том, сколько задач выполнять на каждой GPU. Например, если вы хотите, чтобы Spark выполняла две задачи на GPU, можно указать это следующим образом:

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)

Перераспределите данные для использования всех доступных аппаратных средств

Во-вторых, для повышения производительности важно максимально использовать оборудование в вашем кластере. Как правило, небольшое число, кратное количеству графических процессоров в рабочих узлах (для кластеров GPU) или количеству ядер во всех рабочих узлах в вашем кластере (для кластеров CPU), работает хорошо. Может быть, ваш входной DataFrame уже имеет достаточно партиций, чтобы воспользоваться параллелизмом кластера. Чтобы узнать, сколько секций содержит кадр данных, используйте df.rdd.getNumPartitions(). С помощью repartitioned_df = df.repartition(desired_partition_count) можно перераспределить DataFrame.

Кэширование модели в DBFS или точках подключения

Если вы часто загружаете модель из разных или перезапущенных кластеров, вы также можете кэшировать модель Hugging Face в корневом томе DBFS или на точке монтирования. Это может снизить затраты на входящий трафик и сократить время загрузки модели в новом или перезапущенном кластере. Для этого задайте TRANSFORMERS_CACHE переменную среды в коде перед загрузкой конвейера.

Например:

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

Кроме того, вы можете добиться аналогичных результатов, записав модель в MLflow с помощью варианта MLflow.

Записная книжка: вывод Hugging Face Transformers и логирование в MLflow.

Чтобы быстро начать работу с примером кода, эта записная книжка является полным примером суммирования текста для вывода с помощью конвейеров Hugging Face Transformers и логирования с помощью MLflow.

Блокнот для вывода на основе конвейеров Hugging Face Transformers

Возьмите записную книжку

Дополнительные ресурсы

Вы можете точно настроить свою модель Hugging Face с помощью следующих руководств:

Узнайте больше о Hugging Face Transformers