Bola vydaná prvá verzia FastAPI koncom roka 2018 a odvtedy sa čoraz častejšie používa v mnohých produkčných aplikáciách (pozrite si webovú stránku FastAPI). To je to, čo používame za kapotou v službe NLP Cloud. Je to skvelý spôsob, ako jednoducho a efektívne obsluhovať naše stovky modelov spracovania prirodzeného jazyka, na extrakciu entít (NER), klasifikáciu textov, analýzu nálad, analýzu otázok odpovedanie na otázky, sumarizáciu... Zistili sme, že FastAPI je skvelý spôsob, ako obsluhovať hĺbkové modely založené na transformátoroch učenia.
V tomto článku sme si mysleli, že by bolo zaujímavé ukázať vám, ako implementujeme API na spracovanie prirodzeného jazyka na základe na transformátoroch Hugging Face pomocou FastAPI.
Pred FastAPI sme v podstate používali Django Rest Framework pre naše Python API, ale rýchlo sme FastAPI zaujalo z nasledujúcich dôvodov:
Vďaka týmto skvelým vlastnostiam sa FastAPI dokonale hodí pre rozhrania API pre strojové učenie. modely založené na transformátoroch, ako je ten náš.
Aby FastAPI fungovalo, spájame ho so serverom Uvicorn ASGI, čo je moderný spôsob, ako natívne spracúvať asynchrónne požiadavky jazyka Python pomocou asyncio. Môžete sa rozhodnúť buď pre nainštalovať FastAPI s Uvicorn ručne, alebo si stiahnuť obraz Docker pripravený na použitie. Ukážeme si najprv manuálnu inštaláciu:
pip install fastapi[all]
Potom ho môžete začať:
uvicorn main:app
Sebastián Ramírez, tvorca FastAPI, poskytuje niekoľko hotových obrazov Docker, vďaka ktorým je veľmi používať FastAPI v produkcii. Uvicorn + Gunicorn + FastAPI využíva výhody Gunicornu na paralelné používanie viacerých procesov (Pozrite si obrázok tu). Nakoniec sa vďaka Uvicorn môžete obsluhovať niekoľko inštancií FastAPI v rámci jedného procesu Python a vďaka Gunicorn môžete spustiť niekoľko procesov Python.
Vaša aplikácia FastAPI sa automaticky spustí pri spustení kontajnera Docker s nasledujúcim:
docker run.
Je dôležité, aby ste si riadne prečítali dokumentáciu k týmto obrazom Docker, pretože existujú niektoré nastavenia, ktoré
ako napríklad počet paralelných procesov vytvorených nástrojom Gunicorn. V predvolenom nastavení,
obraz vytvára toľko procesov, koľko jadier procesora má váš počítač. Ale v prípade náročných
modelov strojového učenia, ako sú transformátory spracovania prirodzeného jazyka, to môže rýchlo viesť k desiatkam GB použitej pamäte. Jedna stránka
stratégiou by bolo využiť možnosť Gunicorn (--preload), na načítanie
model do pamäte iba raz a zdieľať ho medzi všetkými procesmi FastAPI Python. Ďalšou možnosťou by bolo
je obmedziť počet procesov Gunicorn. Obe možnosti majú svoje výhody aj nevýhody, ale to je už mimo
rozsahu tohto článku.
Klasifikácia textu je proces určovania, o čom sa v texte hovorí (Priestor? Obchod? O potravinách?...). Viac informácií o texte klasifikácia tu.
Chceme vytvoriť koncový bod API, ktorý vykonáva klasifikáciu textu pomocou aplikácie Bart Large MNLI spoločnosti Facebook ktorý je predtrénovaným modelom založeným na transformátoroch Hugging Face, ktorý sa dokonale hodí na textové na klasifikáciu textov.
Náš koncový bod API prijme ako vstup text spolu s potenciálnymi kategóriami (nazývanými štítky), a vráti skóre pre každú kategóriu (čím vyššie, tým pravdepodobnejšie).
Koncový bod budeme požadovať pomocou POST požiadaviek takto:
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"]
}'
A na oplátku by sme dostali odpoveď ako:
{
"labels": [
"job",
"space",
"nature"
],
"scores": [
0.9258803129196167,
0.19384843111038208,
0.010988432914018631
]
}
Tu je návod, ako to dosiahnuť pomocou FastAPI a 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)
Po prvé: načítame Facebook Bart Large MNLI z úložiska Hugging Face a správne ho inicializujeme na účely klasifikácie vďaka Transformer Pipeline:
classifier = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli")
A neskôr používame model týmto spôsobom:
classifier(user_request_in.text, user_request_in.labels)
Druhá dôležitá vec: vďaka Pydanticu vykonávame validáciu údajov. Pydantic vás núti
vopred deklarovať vstupný a výstupný formát vášho API, čo je z hľadiska dokumentácie skvelé
hľadiska, ale aj preto, že obmedzuje možné chyby. V jazyku Go by ste urobili takmer to isté
s JSON unmarshalling pomocou štruktúr. Tu je jednoduchý spôsob, ako
deklarovať, že pole "text" by malo mať aspoň 1 znak: constr(min_length=1).
A nasledujúci príkaz určuje, že vstupný zoznam štítkov by mal v zozname obsahovať jeden prvok:
conlist(str,
min_items=1).
Tento riadok znamená, že výstupné pole "labels" by malo byť zoznamom reťazcov: List[str].
A to znamená, že skóre by malo byť zoznamom plávajúcich hodnôt: List[float].
Ak model vracia výsledky, ktoré nedodržiavajú tento formát, FastAPI automaticky vyhlási chybu.
class UserRequestIn(BaseModel):
text: constr(min_length=1)
labels: conlist(str, min_items=1)
class ScoredLabelsOut(BaseModel):
labels: List[str]
scores: List[float]
Napokon, nasledujúci dekorátor umožňuje ľahko určiť, že prijímate len požiadavky POST na konkrétnom koncovom bode:
@app.post("/entities", response_model=EntitiesOut).
Môžete vykonávať mnoho zložitejších overovacích úkonov, ako je napríklad zloženie. Povedzme napríklad, že
robíte rozpoznávanie pomenovaných entít (NER), takže váš model vracia zoznam entít. Každá entita
by mala 4 polia: text, type, start a position. Tu je návod, ako to môžete urobiť:
class EntityOut(BaseModel):
start: int
end: int
type: str
text: str
class EntitiesOut(BaseModel):
entities: List[EntityOut]
@app.post("/entities", response_model=EntitiesOut)
# [...]
Doteraz sme overovanie nechávali na Pydantic. Vo väčšine prípadov to funguje, ale niekedy môžete chcieť dynamicky vyvolať chybu sami na základe zložitých podmienok, ktoré nie sú natívne spracované Pydantic. Ak chcete napríklad ručne vrátiť chybu HTTP 400, môžete postupovať takto:
from fastapi import HTTPException
raise HTTPException(status_code=400,
detail="Your request is malformed")
Samozrejme, že môžete urobiť oveľa viac!
Ak používate FastAPI za reverzným proxy serverom, pravdepodobne sa budete musieť pohrať s koreňovou cestou.
Problémom je, že za reverzným proxy serverom aplikácia nepozná celú cestu URL, takže jej musíme explicitne povedať, ktorá to je.
Napríklad tu nemusí byť úplná adresa URL nášho koncového bodu jednoducho /classification. Ale mohlo by to byť niečo ako /api/v1/classification. Túto úplnú adresu URL nechceme zakódovať napevno, aby
aby bol náš kód API voľne previazaný so zvyškom aplikácie. Mohli by sme to urobiť takto:
app = FastAPI(root_path="/api/v1")
Prípadne môžete pri spustení programu Uvicorn odovzdať parameter:
uvicorn main:app --root-path /api/v1
Dúfam, že sme vám úspešne ukázali, aké pohodlné môže byť FastAPI pre API na spracovanie prirodzeného jazyka. Pydantic umožňuje kód veľmi expresívny a menej náchylný na chyby.
FastAPI má skvelý výkon a umožňuje používať asyncio Python z krabice, čo je skvelé pre náročné modely strojového učenia, ako sú modely spracovania prirodzeného jazyka založené na transformátore. FastAPI sme používali pre NLP Cloud takmer 1 rok a zatiaľ sme nikdy neboli sklamaní.
Ak máte akékoľvek otázky, neváhajte sa opýtať, bude nám potešením sa k nim vyjadriť!
Julien Salinas
Technický riaditeľ spoločnosti NLP Cloud