Первая версия FastAPI была выпущена к концу 2018 года, и с тех пор она все чаще используется во многих производственных приложениях (см. веб-сайт FastAPI). Это то, что мы используем в NLP Cloud. Это отличный способ легко и эффективно обслуживать наши сотни моделей обработки естественного языка, для извлечения сущностей (NER), классификации текстов, анализа настроений, ответов на вопросы, обобщения... ответы на вопросы, резюмирование... Мы обнаружили, что FastAPI - отличный способ обслуживать модели глубокого обучения на основе трансформаторов. модели глубокого обучения на основе трансформаторов.
В этой статье мы решили, что будет интересно показать вам, как мы реализуем API обработки естественного языка на основе на трансформаторах Hugging Face с помощью FastAPI.
До FastAPI мы в основном использовали Django Rest Framework для наших Python API, но мы быстро заинтересовались FastAPI по следующим причинам:
Такие высокие показатели делают FastAPI идеально подходящим для API машинного обучения, обслуживающих модели на основе трансформаторов, такие как наша. модели на основе трансформаторов, как у нас.
Чтобы FastAPI работал, мы соединяем его с сервером ASGI от Uvicorn, который является современным способом естественной обработки асинхронных запросов Python с помощью asyncio. Вы можете либо решить установить FastAPI с Uvicorn вручную или загрузить готовый к использованию образ Docker. Давайте покажем ручную установку:
pip install fastapi[all]
Тогда вы можете начать с:
uvicorn main:app
Себастьян Рамирес, создатель FastAPI, предоставляет несколько готовых к использованию образов Docker, которые делают использование FastAPI очень легко использовать FastAPI в производстве. Образ Uvicorn + Gunicorn + FastAPI образ использует преимущества Gunicorn для параллельного использования нескольких процессов (смотрите изображение здесь). В итоге, благодаря Uvicorn вы можете работать с несколькими экземплярами FastAPI в рамках одного процесса Python, а благодаря Gunicorn вы можете порождать несколько процессов Python.
Ваше приложение FastAPI автоматически запустится при запуске контейнера Docker со следующим:
docker run.
Важно правильно прочитать документацию к этим образам Docker, поскольку есть некоторые параметры, которые вы
например, количество параллельных процессов, создаваемых Gunicorn. По умолчанию
образ порождает столько процессов, сколько ядер процессора на вашей машине. Но в случае требовательных
моделей машинного обучения, таких как трансформаторы обработки естественного языка, это может быстро привести к десяткам гигабайт используемой памяти. Одна из
стратегия заключается в использовании опции Gunicorn (--preload), для того, чтобы загрузить
вашу модель только один раз в память и разделить ее между всеми процессами FastAPI Python. Другим вариантом может быть
ограничить количество процессов Gunicorn. Оба варианта имеют свои преимущества и недостатки, но это выходит за рамки данной статьи.
рамки данной статьи.
Классификация текста - это процесс определения того, о чем идет речь в тексте (Космос? Бизнес? Еда?...). Более подробно о тексте классификация здесь.
Мы хотим создать конечную точку API, выполняющую классификацию текста с помощью модели Facebook Bart Large MNLI модель, которая представляет собой предварительно обученную модель, основанную на трансформаторах Hugging Face, идеально подходящую для классификации текста. классификации.
Наша конечная точка API будет принимать фрагмент текста в качестве входных данных, а также потенциальные категории (называемые метками), и возвращает оценку для каждой категории (чем выше, тем больше вероятность).
Мы будем запрашивать конечную точку с помощью POST-запросов следующим образом:
curl "https://api.nlpcloud.io/v1/bart-large-mnli/classification" \
-H "Authorization: Token e7f6539e5a5d7a16e15" \
-X POST -d '{
"text":"John Doe is a Go Developer at Google. He has been working there for 10 years and has been awarded employee of the year.",
"labels":["job", "nature", "space"]
}'
И в ответ мы получаем ответ типа:
{
"labels": [
"job",
"space",
"nature"
],
"scores": [
0.9258803129196167,
0.19384843111038208,
0.010988432914018631
]
}
Вот как этого добиться с помощью FastAPI и трансформеров:
from fastapi import FastAPI
from pydantic import BaseModel, constr, conlist
from typing import List
from transformers import pipeline
classifier = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli")
app = FastAPI()
class UserRequestIn(BaseModel):
text: constr(min_length=1)
labels: conlist(str, min_items=1)
class ScoredLabelsOut(BaseModel):
labels: List[str]
scores: List[float]
@app.post("/classification", response_model=ScoredLabelsOut)
def read_classification(user_request_in: UserRequestIn):
return classifier(user_request_in.text, user_request_in.labels)
Прежде всего: мы загружаем Facebook's Bart Large MNLI из репозитория Hugging Face и правильно инициализируем его для целей классификации, благодаря Transformer Pipeline:
classifier = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli")
И в дальнейшем мы используем модель, делая это:
classifier(user_request_in.text, user_request_in.labels)
Второй важный момент: мы выполняем валидацию данных благодаря Pydantic. Pydantic заставляет вас
заранее объявить входной и выходной формат для вашего API, что отлично с точки зрения документации
с точки зрения документации, но также и потому, что это ограничивает возможные ошибки. В Go вы бы сделали практически то же самое
с разгруппировкой JSON с помощью структур. Вот простой способ
объявить, что поле "text" должно содержать по крайней мере 1 символ: constr(min_length=1).
А следующий определяет, что входной список меток должен содержать один элемент:
conlist(str,
min_items=1).
Эта строка означает, что поле вывода "labels" должно представлять собой список строк: List[str].
А этот означает, что оценки должны быть списком плавающих элементов: List[float].
Если модель возвращает результаты, которые не соответствуют этому формату, FastAPI автоматически выдает ошибку.
class UserRequestIn(BaseModel):
text: constr(min_length=1)
labels: conlist(str, min_items=1)
class ScoredLabelsOut(BaseModel):
labels: List[str]
scores: List[float]
Наконец, следующий декоратор позволяет легко указать, что вы принимаете только POST-запросы на определенной конечной точке:
@app.post("/entities", response_model=EntitiesOut).
Вы можете делать гораздо более сложные проверки, такие как, например, композиция. Например, предположим, что
вы выполняете распознавание именованных сущностей (NER), поэтому ваша модель возвращает список сущностей. Каждая сущность
будет иметь 4 поля: text, type, start и position. Вот как вы можете это сделать:
class EntityOut(BaseModel):
start: int
end: int
type: str
text: str
class EntitiesOut(BaseModel):
entities: List[EntityOut]
@app.post("/entities", response_model=EntitiesOut)
# [...]
До сих пор мы позволяли Pydantic выполнять валидацию. Это работает в большинстве случаев, но иногда вы можете захотеть динамически вызывать ошибку самостоятельно, основываясь на сложных условиях, которые не могут быть обработаны Pydantic. Pydantic. Например, если вы хотите вручную вернуть ошибку HTTP 400, вы можете сделать следующее:
from fastapi import HTTPException
raise HTTPException(status_code=400,
detail="Your request is malformed")
Конечно, вы можете сделать гораздо больше!
Если вы используете FastAPI за обратным прокси-сервером, вам, скорее всего, придется поиграть с корневым путем.
Сложность заключается в том, что за обратным прокси приложение не знает всего пути URL, поэтому мы должны явно указать ему, какой именно.
Например, здесь полный URL нашей конечной точки может быть не просто /classification. Но это может быть что-то вроде /api/v1/classification. Мы не хотим жестко кодировать этот полный URL-адрес для того, чтобы
чтобы код нашего API был свободно связан с остальной частью приложения. Мы можем сделать следующее:
app = FastAPI(root_path="/api/v1")
Или же вы можете передать параметр Uvicorn при запуске:
uvicorn main:app --root-path /api/v1
Надеюсь, мы успешно показали вам, насколько удобным может быть FastAPI для API обработки естественного языка. Pydantic делает код очень выразительным и менее подверженным ошибкам.
FastAPI имеет отличную производительность и позволяет использовать Python asyncio из коробки, что очень удобно для требовательных моделей машинного обучения, таких как модели обработки естественного языка на основе трансформаторов. Мы используем FastAPI уже почти 1 год в NLP Cloud и до сих пор ни разу не были разочарованы.
Если есть вопросы, пожалуйста, не стесняйтесь спрашивать, буду рад комментариям!
Julien Salinas
Технический директор NLP Cloud