Objavljena je bila prva različica vmesnika FastAPI konec leta 2018 in od takrat se vse pogosteje uporablja v številnih aplikacijah v produkciji. (Oglejte si spletno mesto FastAPI). To je kar uporabljamo v oblaku NLP Cloud. To je odličen način za enostavno in učinkovito služenje našim stotine modelov obdelave naravnega jezika za ekstrakcijo entitet (NER), klasifikacijo besedil, analizo čustev, analizo vprašanj odgovarjanje na vprašanja, povzemanje... Ugotovili smo, da je FastAPI odličen način, da postrežemo z globokimi modeli, ki temeljijo na transformatorjih. modeli učenja.
V tem članku bi bilo zanimivo prikazati, kako izvajamo API za obdelavo naravnega jezika, ki temelji na transformatorjih Hugging Face s FastAPI.
Pred FastAPI smo za svoje API-je v Pythonu v osnovi uporabljali Django Rest Framework, vendar smo hitro zanimal FastAPI iz naslednjih razlogov:
Zaradi teh odličnih zmogljivosti je FastAPI popolnoma primeren za API za strojno učenje. modelov, ki temeljijo na transformatorjih, kot je naš.
Da bi FastAPI deloval, ga povezujemo s strežnikom Uvicorn ASGI, ki je sodoben način za nativno obdelavo asinhronih zahtevkov Python z asyncio. Lahko se odločite za FastAPI z Uvicornom namestite ročno ali pa prenesete sliko Docker, ki je pripravljena za uporabo. Pokažimo najprej ročno namestitev:
pip install fastapi[all]
Nato ga lahko začnete z:
uvicorn main:app
Sebastián Ramírez, ustvarjalec vmesnika FastAPI, ponuja več slik Docker, ki so pripravljene za uporabo in s katerimi je mogoče uporabo FastAPI v produkciji. Uvicorn + Gunicorn + FastAPI izkorišča prednosti sistema Gunicorn za vzporedno uporabo več procesov (Oglejte si sliko tukaj). Na koncu se je zahvaljujoč Uvicorn lahko upravljate več instanc FastAPI znotraj istega procesa Python, in zahvaljujoč Gunicorn lahko sprožite več procesov Python.
Vaša aplikacija FastAPI se bo samodejno zagnala ob zagonu vsebnika Docker z naslednjim:
docker run.
Pomembno je, da pravilno preberete dokumentacijo teh slik Docker, saj so nekatere nastavitve, ki jih
kot je na primer število vzporednih procesov, ki jih ustvari Gunicorn. Privzeto,
slika ustvari toliko procesov, kolikor je procesorskih jeder v vašem računalniku. Toda v primeru zahtevnih
modelov strojnega učenja, kot so pretvorniki za obdelavo naravnega jezika, lahko to hitro privede do več deset GB porabljenega pomnilnika. Ena stran
strategija bi bila uporaba možnosti Gunicorn (--preload), za nalaganje
model v pomnilnik naložite samo enkrat in ga delite med vse procese FastAPI Python. Druga možnost je
je omejiti število procesov Gunicorn. Obe možnosti imata prednosti in slabosti, vendar to presega okvire
obsega tega članka.
Klasifikacija besedila je postopek določanja, o čem govori besedilo (Prostor? O čem? Hrana?...). Več podrobnosti o besedilu razvrstitev tukaj.
Želimo ustvariti končno točko API, ki izvaja razvrščanje besedila z uporabo Facebookovega Bart Large MNLI ki je vnaprej usposobljen model, ki temelji na transformatorjih Hugging Face in je popolnoma primeren za besedilo. za razvrščanje besedil.
Naša končna točka API bo kot vhodni podatek sprejela del besedila in morebitne kategorije (imenovane oznake), in vrne oceno za vsako kategorijo (čim višja je, tem večja je verjetnost).
Končno točko bomo zahtevali z zahtevki POST, kot sledi:
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"]
}'
V zameno smo dobili odgovor, kot je:
{
"labels": [
"job",
"space",
"nature"
],
"scores": [
0.9258803129196167,
0.19384843111038208,
0.010988432914018631
]
}
Tukaj je opisano, kako to doseči s FastAPI in transformatorji:
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)
Najprej: naložimo Facebookov Bart Large MNLI iz repozitorija Hugging Face in in ga pravilno inicializiramo za namene klasifikacije, zahvaljujoč transformatorskemu cevovodu:
classifier = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli")
Kasneje pa s tem uporabljamo model:
classifier(user_request_in.text, user_request_in.labels)
Druga pomembna stvar: s pomočjo programa Pydantic preverjamo podatke. Pydantic vas prisili, da
vnaprej prijaviti vhodni in izhodni format za vaš API, kar je odlično z vidika dokumentacije.
vidika, pa tudi zato, ker omejuje morebitne napake. V jeziku Go bi naredili skoraj enako stvar
z razčlenjevanjem JSON s strukturami. Tukaj je enostaven način za
izjavite, da mora polje "besedilo" vsebovati vsaj 1 znak: constr(min_length=1).
V nadaljevanju pa je določeno, da mora vhodni seznam nalepk na seznamu vsebovati en element:
conlist(str,
min_items=1).
Ta vrstica pomeni, da mora biti izhodno polje "labels" seznam nizov: List[str].
Ta pomeni, da morajo biti rezultati seznam plavajočih elementov: List[float].
Če model vrne rezultate, ki ne ustrezajo tej obliki, FastAPI samodejno sporoči napako.
class UserRequestIn(BaseModel):
text: constr(min_length=1)
labels: conlist(str, min_items=1)
class ScoredLabelsOut(BaseModel):
labels: List[str]
scores: List[float]
Nazadnje, z naslednjim dekoratorjem lahko preprosto določite, da sprejemate samo zahteve POST na določeni končni točki:
@app.post("/entities", response_model=EntitiesOut).
Izvedete lahko veliko bolj zapletenih postopkov potrjevanja, kot je na primer sestavljanje. Recimo, da
izvajate prepoznavanje poimenovanih entitet (NER), torej vaš model vrača seznam entitet. Vsaka entiteta
ima 4 polja: text, type, start in . position. To lahko storite na naslednji način:
class EntityOut(BaseModel):
start: int
end: int
type: str
text: str
class EntitiesOut(BaseModel):
entities: List[EntityOut]
@app.post("/entities", response_model=EntitiesOut)
# [...]
Do zdaj smo potrjevanje prepuščali podjetju Pydantic. To v večini primerov deluje, vendar včasih želite sami dinamično sprožiti napako na podlagi zapletenih pogojev, ki jih ne obvladuje Pydantic. Če želite na primer ročno vrniti napako HTTP 400, lahko storite naslednje:
from fastapi import HTTPException
raise HTTPException(status_code=400,
detail="Your request is malformed")
Seveda lahko storite še veliko več!
Če FastAPI uporabljate za obrnjenim posrednikom, se boste najverjetneje morali poigrati s korensko potjo.
Težava je v tem, da za povratnim posrednikom aplikacija ne pozna celotne poti URL, zato ji moramo izrecno povedati, katera je to.
Tu na primer celoten naslov URL do naše končne točke ne sme biti preprosto /classification. Lahko pa je nekaj takega. /api/v1/classification. Tega polnega naslova URL ne želimo zakodirati, da bi
da bi bila naša koda API ohlapno povezana s preostalo aplikacijo. Lahko bi naredili to:
app = FastAPI(root_path="/api/v1")
Lahko pa Uvicornu ob zagonu posredujete parameter:
uvicorn main:app --root-path /api/v1
Upam, da smo vam uspešno pokazali, kako priročen je lahko FastAPI za API za obdelavo naravnega jezika. Pydantic omogoča kodo zelo izrazita in manj nagnjena k napakam.
FastAPI ima odlične zmogljivosti in omogoča uporabo Python asyncio iz škatle, kar je odlično za zahtevne modele strojnega učenja, kot so modeli za obdelavo naravnega jezika, ki temeljijo na transformatorju. FastAPI uporabljamo že skoraj eno leto v NLP Cloud in do zdaj še nikoli nismo bili razočarani.
Če imate kakršno koli vprašanje, ne oklevajte in vprašajte, v veselje vam bo komentirati!
Julien Salinas
Tehnični direktor v podjetju NLP Cloud