Перша версія 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's 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 та Transformers:
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)
Про все по порядку: завантажуємо MNLI Facebook's Bart Large з репозиторію 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
CTO в NLP Cloud