Snelle model-deployment met FastAPI

Snelle model-deployment met FastAPI

Elke Data Scientist kent het wel: Je hebt uren geëxperimenteerd met je data, verschillende modellen getraind en geëvalueerd, en een geweldige oplossing gebouwd voor je probleem. Je uiteindelijke model staat klaar, maar hoe gaat de eindgebruiker dit nu in gebruik kunnen nemen? En niet voor één gebruiker of applicatie, maar misschien wel duizenden tegelijk die real-time voorspellingen verwachten!

In deze blogpost gaan we kijken naar het ontsluiten van een machine learning model via een API. Dit zullen we doen met een relatief nieuw framework: FastAPI. Eerst zal kort worden toegelicht wat FastAPI anders maakt dan Flask of Django, gevolgd door code fragmenten om een eigen API te bouwen. Let’s get started!

Waarom FastAPI?

FastAPI is vergelijkbaar met andere frameworks zoals Flask of Django. FastAPI is zoals de naam doet vermoeden echter een stuk sneller, zowel qua performance en development tijd. De performance is zoveel beter omdat het gebruik maakt van ASGI ((Asynchronous Server Gateway Interface), ten opzichte van Flask, wat gebruik maakt van WSGI (Web Server Gateway Interface). Met WSGI worden alle binnenkomende requests één voor één afgehandeld. Er wordt pas met de volgende request begonnen als de huidige volledig is afgehandeld. Met ASGI hoeven requests echter niet op elkaar te wachten aangezien ze asynchronous worden afgehandeld. De server kan dus met meerdere requests tegelijk bezig zijn, wat het dus een stuk sneller maakt wanneer er veel verkeer is naar de applicatie. 

Daarnaast maakt FastAPI development ook erg makkelijk. Het is makkelijker te debuggen door de mogelijkheid om aanpassingen te doen zonder de applicatie te hoeven herstarten, het genereert automatisch documentatie volgens de OpenAPI standaard en je hoeft niet door extra hoepels te springen voor interactieve documentatie met bijvoorbeeld Swagger. 

Als laatste maakt FastAPI gebruik van Pydantic voor validatie van data, zij het enkele variabelen in de url of diep geneste Json objecten. 

Omdat het framework zo jong is, is er echter niet zo een grote community van gebruikers ten opzichte van Flask of Django. Dit kan het bouwen van complexere applicaties lastiger maken. De genoemde voordelen maken FastAPI echter zeker het overwegen waard, vooral als er veel data en requests heen en weer gaan.

Een API met een paar regels code

Om te beginnen moeten we FastAPI installeren met het volgende commando:

pip install “fastapi[all]”
FastAPI commando

Dit installeert ook uvicorn, wat we nodig hebben om onze API lokaal te draaien.

Vervolgens maken we een main.py file aan met de volgende code:

from fastapi import FastAPI

app = FastAPI()


@app.get(“/”)
async def root():
    return {“message”: “Hello World”}
main.py file

We starten de API met het volgende commando:

uvicorn main:app –reload
API commando

That’s it! We kunnen de interactieve documentatie vinden achter de volgende url: http://127.0.0.1:8000/docs. Ook kunnen we dit gebruiken om een request uit te voeren. 

We willen ook data kunnen verzenden in de vorm van een POST request. Ook dit is heel simpel met FastAPI. We voegen de volgende functie toe om een verzonden nummer te verdubbelen en dit vervolgens terug te geven:

@app.post(“/double/{number}”)
async def double_number(number: int):
    return {“message”: number*2}
Verzonden nummer verdubbelen

Zoals je ziet hebben we het type van number aangegeven in de functie definitie. Als de API iets anders dan een integer wordt verstuurd naar dit path, zal de API een error teruggeven. De SwaggerUI staat ons niet eens toe een request uit te voeren met incorrecte data!

SwaggerUI

We kunnen zelfs nog meer limieten aangeven, zoals dat het getal positief moet zijn. Als eerste moeten we Path importeren:

from fastapi import FastAPI, Path
Path

Onze functie wordt dan:

@app.post(“/double/{number}”)
async def double_number(number: int = Path(description=”Double a positive number.”, gt=0)):
    return {“message”: number*2}
Functie

Met Path kunnen we extra restricties en ook metadata declareren voor een variabele. Met gt=0 geven we aan dat het groter dan nul moet zijn. Ook hebben we een description toegevoegd. Deze zie je terug in de documentatie:

description

Als we een negatief getal invoeren krijgen we nu het volgende terug van de API:

negatief getal

Alle validaties worden gedaan door Pydantic op de achtergrond. Dit zorgt ervoor dat we reguliere Python type hinting kunnen gebruiken om de types aan te geven. Dit heeft ook weer als voordeel dat we hints en autocompletion krijgen in editors als Pycharm.

We kunnen Pydantic ook gebruiken om automatisch Json objecten te valideren, bijvoorbeeld als we iets willen meesturen in de body van een request. Dit doen we door een Pydantic model definiëren:

from pydantic import BaseModel


class NumbersModel(BaseModel):
    number1: int
    number2: int
Pydantic model definiëren

Vervolgens voegen we de volgende functie toe, waarbij we vragen om een NumbersModel als input:

@app.post(“/add/”)
async def add_numbers(numbers: NumbersModel):
    return {“message”: numbers.number1 + numbers.number2}
Input NumbersModel

FastAPI snapt automatisch dat het voor numbers in de body van de request moet kijken. Als je deze functie in een editor als Pycharm gaat schrijven, zul je ook opmerken dat je autocompletion hebt voor de attributen van NumbersModel. Daarnaast wordt de SwaggerUI ook automatisch geüpdatet met een voorbeeld:

SwaggerUI update

Al met al is het dus enorm eenvoudig om snel een API in elkaar te zetten met FastAPI. Verander de simpele sommetjes in een aanroep van je machine learning model en je hebt je eerste stappen gezet richting een daadwerkelijk bruikbaar en schaalbaar product!