FastAPI esimene versioon on avaldatud 2018. aasta lõpus ja seda on sellest ajast alates üha enam kasutatud paljudes tootmises olevates rakendustes (vt FastAPI veebisait). See on mida me kasutame NLP Cloudi kapoti taga. See on suurepärane viis, kuidas lihtsalt ja tõhusalt teenindada meie sadu loodusliku keeletöötluse mudeleid, mis on mõeldud olemuste ekstraheerimiseks (NER), tekstide klassifitseerimiseks, tunnetusanalüüsiks, küsimuste vastamine, kokkuvõtete tegemine... Leidsime, et FastAPI on suurepärane võimalus teenida transformaatoripõhiseid sügavaid õppemudeleid.
Selles artiklis mõtlesime, et oleks huvitav näidata, kuidas me rakendame loomulikul keeletöötlusel põhinevat API-d. Hugging Face'i transformaatoritel koos FastAPIga.
Enne FastAPI-d kasutasime oma Python API-de jaoks sisuliselt Django Rest Framework'i, kuid olime kiiresti huvitatud FastAPI-st järgmistel põhjustel:
Tänu nendele suurepärastele näitajatele sobib FastAPI suurepäraselt masinõppe API-de jaoks, mis teenivad trafopõhiseid mudeleid nagu meie oma.
Selleks, et FastAPI töötaks, ühendame selle Uvicorn ASGI serveriga, mis on kaasaegne viis, kuidas asünkroonseid Pythoni päringuid asyncio abil natiivselt käsitleda. Võite kas otsustada, et paigaldada FastAPI koos Uvicorniga käsitsi või laadida alla kasutusvalmis Dockeri image. Näitame esmalt käsitsi paigaldamist:
pip install fastapi[all]
Siis võite alustada seda:
uvicorn main:app
Sebastián Ramírez, FastAPI looja, pakub mitmeid kasutusvalmis Dockeri kujutisi, mis teevad selle väga FastAPI kasutamine tootmises on lihtne. Uvicorn + Gunicorn + FastAPI image kasutab ära Gunicorn'i, et kasutada paralleelselt mitut protsessi (vaata pilti siit). Lõpuks, tänu Uvicornile saab ühe ja sama Pythoni protsessi raames käsitleda mitut FastAPI instantsi ja tänu Gunicornile saate luua mitu Python-protsessi.
Teie FastAPI rakendus käivitub automaatselt Dockeri konteineri käivitamisel järgmiselt:
docker run.
Oluline on lugeda korralikult nende Docker-kujutiste dokumentatsiooni, kuna seal on mõned seaded, mida te
mida võib olla vaja muuta, näiteks Gunicorni poolt loodud paralleelsete protsesside arvu. Vaikimisi,
genereerib kujutis nii palju protsesse, kui palju on teie masina protsessori südamikke. Kuid nõudliku kasutuse korral
masinõppemudelite, näiteks loodusliku keeletöötluse muundurite puhul võib see kiiresti viia kümnete GB kasutatud mäluni. Üks
strateegia oleks kasutada Gunicorni võimalust (--preload), et laadida
oma mudelit ainult üks kord mällu ja jagada seda kõigi FastAPI Pythoni protsesside vahel. Teine võimalus oleks
oleks piirata Gunicorn-protsesside arvu. Mõlemal on nii eeliseid kui ka puudusi, kuid see on väljaspool
selle artikli reguleerimisalasse.
Teksti klassifitseerimine on protsess, mille käigus määratakse kindlaks, millest tekstis räägitakse (Space? Äri? Toidust?...). Rohkem üksikasju teksti kohta klassifitseerimine siin.
Me tahame luua API-punkti, mis teostab teksti klassifitseerimist, kasutades Facebooki Bart Large MNLI-d. mudelit, mis on eelnevalt treenitud mudel, mis põhineb Hugging Face transformaatoritel ja sobib ideaalselt teksti klassifitseerimiseks.
Meie API lõpp-punkt võtab sisendiks teksti koos võimalike kategooriatega (mida nimetatakse siltideks), ja tagastab iga kategooria jaoks skoori (mida kõrgem, seda tõenäolisem).
Me taotleme lõpp-punkti POST päringutega järgmiselt:
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"]
}'
Ja vastuseks saaksime vastuse nagu:
{
"labels": [
"job",
"space",
"nature"
],
"scores": [
0.9258803129196167,
0.19384843111038208,
0.010988432914018631
]
}
Siin on, kuidas seda FastAPI ja Transformersi abil saavutada:
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)
Esmalt: laadime Facebooki Bart Large MNLI'i Hugging Face'i repositooriumist ja initsialiseerime selle korralikult klassifitseerimise eesmärgil, tänu Transformer Pipeline'ile:
classifier = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli")
Ja hiljem kasutame mudelit, tehes seda:
classifier(user_request_in.text, user_request_in.labels)
Teine oluline asi: me teostame andmete valideerimist tänu Pydanticule. Pydantic sunnib teid
eelnevalt deklareerima oma API sisend- ja väljundformaadi, mis on dokumentatsiooni seisukohalt suurepärane
seisukohast, aga ka seetõttu, et see piirab võimalikke vigu. Go's teeksite üsna täpselt sama asja
JSONi unmarshallinguga structidega. Siin on lihtne viis
deklareerida, et väljal "text" peaks olema vähemalt 1 märk: constr(min_length=1).
Ja järgmine määrab, et sisendloend siltide loendis peaks loendis olema üks element:
conlist(str,
min_items=1).
See rida tähendab, et väljundväli "labels" peaks olema stringide nimekiri: List[str].
Ja see tähendab, et hinded peaksid olema ujujate nimekiri: List[float].
Kui mudel tagastab tulemused, mis ei vasta sellele vormingule, annab FastAPI automaatselt veateate.
class UserRequestIn(BaseModel):
text: constr(min_length=1)
labels: conlist(str, min_items=1)
class ScoredLabelsOut(BaseModel):
labels: List[str]
scores: List[float]
Viimasena saab järgmise dekoraatori abil hõlpsasti määrata, et te võtate vastu ainult POST-päringuid konkreetses lõpp-punktis:
@app.post("/entities", response_model=EntitiesOut).
Saate teha palju keerulisemaid valideerimisega seotud asju, nagu näiteks kompositsioon. Ütleme näiteks, et
te teete Named Entity Recognition (NER), nii et teie mudel tagastab olemite nimekirja. Iga üksus
oleks 4 välja: text, type, start ja position. Seda saab teha järgmiselt:
class EntityOut(BaseModel):
start: int
end: int
type: str
text: str
class EntitiesOut(BaseModel):
entities: List[EntityOut]
@app.post("/entities", response_model=EntitiesOut)
# [...]
Seni oleme lasknud Pydanticul valideerimisega tegeleda. See toimib enamikul juhtudel, kuid mõnikord võib soovida, et ise dünaamiliselt viga tekitada, mis põhineb keerulistel tingimustel, mida Pydantyd ei saa algselt käsitleda. Pydantic. Näiteks kui soovite käsitsi tagastada HTTP 400 vea, saate teha järgmist:
from fastapi import HTTPException
raise HTTPException(status_code=400,
detail="Your request is malformed")
Loomulikult saate teha palju rohkem!
Kui kasutate FastAPI-d pöördproxy taga, peate tõenäoliselt mängima juurtepolguga.
Keeruline on see, et pöördproxy taga ei tea rakendus kogu URL-posti teekonda, nii et me peame talle selgesõnaliselt ütlema, milline see on.
Näiteks siin ei pruugi täielik URL meie lõpp-punktile olla lihtsalt /classification. Aga see võib olla midagi sellist nagu /api/v1/classification. Me ei taha seda täielikku URL-i kõvasti sisse kodeerida, et
meie API-kood oleks lõdvalt seotud ülejäänud rakendusega. Me võiksime teha nii:
app = FastAPI(root_path="/api/v1")
Või alternatiivina võiksite Uvicornile selle käivitamisel anda parameetri:
uvicorn main:app --root-path /api/v1
Ma loodan, et me näitasime teile edukalt, kui mugav FastAPI võib olla loomuliku keele töötlemise API jaoks. Pydantic teeb koodi väga väljendusrikkaks ja vähem veaohtlikuks.
FastAPI on suurepärase jõudlusega ja võimaldab kasutada Python asyncio out of the box, mis on suurepärane. nõudlike masinõppe mudelite, näiteks Transformeril põhinevate loodusliku keele töötlemise mudelite jaoks. Me oleme kasutanud FastAPId peaaegu 1 aasta NLP Cloud's ja me pole seni kunagi pettunud.
Kui mõni küsimus, palun ärge kartke küsida, see on rõõm kommenteerida!
Julien Salinas
NLP Cloud tehnoloogiajuht