[PYTHON] Inferenz des maschinellen Lernmodells Web-API-Serverkonfiguration [Beispiel für eine schnelle API-Implementierung verfügbar]

Zweck dieses Artikels

Einführung in eine typische Konfiguration der Inferenz-Web-API für maschinelles Lernen. Ich denke, dass der Inhalt gelesen werden kann, ohne unbedingt Kenntnisse über das WEB oder maschinelles Lernen zu haben. (Ohne Implementierungsbeispiele) Die einzuführende Komposition basiert auf der Erfahrung mit der Erstellung von Inferenz-Web-APIs für einige Modelle des maschinellen Lernens in Unternehmen. Da es jedoch meine persönliche Meinung ist, lassen Sie es mich in den Kommentaren wissen, wenn es etwas Besseres gibt Ich bin glücklich. Im Implementierungsbeispiel verwendet das Webframework die Fast-API unter dem Gesichtspunkt der einfachen Handhabung der asynchronen Verarbeitung und der Einfachheit der Implementierung.

Inhaltsverzeichnis

  1. Konfiguration der Inferenz-Web-API für maschinelles Lernen
  2. Implementierungsbeispiel

1. Konfiguration der Inferenz-Web-API für maschinelles Lernen

In diesem Artikel werde ich zwei Muster vorstellen.

Hinweis) Zunächst werde ich die gemeinsamen Teile erläutern. Kenntnisse über maschinelles Lernen sind grundsätzlich nur für die gemeinsamen Teile erforderlich. Wenn Sie mit maschinellem Lernen oder dem Web nicht vertraut sind, können Sie die Rollen zwischen dem gemeinsamen Teil und dem später beschriebenen Teil aufteilen, damit Sie ihn fließen lassen können.

Inferenz-API (gemeinsamer Teil)

Wenn das trainierte Modell abgeleitet werden soll, erstellen Sie im Allgemeinen die folgende Inferenz-API für maschinelles Lernmodell. Selbst wenn Sie nur auf einem lokalen PC oder Jupyter Notebook entwickeln, werden Sie wahrscheinlich eine solche API (Pipeline) erstellen.

Ich werde die Details weglassen, aber zur Vereinfachung der Lastverteilung und des Modellmanagements denke ich, dass Sie nur die API ausschneiden können, die das maschinelle Lernmodell für den Server in der Cloud verwendet (Referenz: [GCP AI Platform Prediction](https: /) /cloud.google.com/ai-platform/prediction/docs)). Bei einem schweren Modell, bei dem Leistungsprobleme auftreten, wenn die GPU nicht nur zum Laden, sondern auch zum Ableiten verwendet wird, ist es nicht möglich, sie mit einem Server für gängige WEB-Anwendungen zu verarbeiten. Daher halte ich es für flexibler, sie isolieren zu können. Ich denke auch, dass dieselbe Konfiguration verwendet wird, wenn ein externer Dienst verwendet wird, der ein geschultes Modell verwendet.

online_vs_batch-Copy of online prediction API.png

Ich denke, dass es mit zunehmender Datenmenge erforderlich sein wird, Maßnahmen wie das Ersetzen der Vorverarbeitung durch eine umfangreiche Datenverarbeitungs-Engine wie Google Cloud Dataflow zu ergreifen.

Beim Erstellen einer Web-API basierend auf der Inferenz-API, die auf einem lokalen PC oder Jupyter Notebook wie oben beschrieben entwickelt wurde, können hauptsächlich zwei Arten von Mustern berücksichtigt werden. Diese behandeln Eingabe- und Ausgabedaten unterschiedlich.

--1.1. Online-Vorhersage (auch als HTTP-Vorhersage bezeichnet) --1.2 Chargenvorhersage

(Der in der AI-Plattform von GCP verwendete Name wird verwendet. Referenz: [Online-Vorhersage vs. Batch-Vorhersage](https://cloud.google.com/ml-engine/docs/tensorflow/online-vs-batch- Vorhersage? hl = ja))

1.1 Online-Prognose

online prediction API.png

Wenn eine http-Anforderung eintrifft, wird die ML-Funktion ausgeführt und die Ausgabe wird sofort von der http-Antwort zurückgegeben. Dies ist eine einfache Konfiguration. Laden Sie das Gewicht nur einmal, wenn der Server gestartet wird. Beim Laden von Gewichten ist es einfacher, das Modell zu ändern, wenn Sie die Gewichte aus dem Cloud-Speicher (Google Storage usw.) beziehen.

Vorteil

Nachteil

1.2 Chargenvorhersage

batch prediction API.png

Wenn die Antwort nicht sofort zurückgegeben werden kann oder nicht zurückgegeben werden muss, wird das Inferenzergebnis der ML-API in einem Speicher gespeichert, ohne direkt zu antworten, wie unten gezeigt. Der Prozess kann wie unten gezeigt in drei Stufen unterteilt werden. (Es ist gut, wenn 2 und 3 getrennt sind. Die Upload-API kann in die ML-API integriert werden.)

  1. Upload-API: Speichern Sie Daten für die Eingabe im Speicher (Datenbank, Cloud-Speicher usw.).
  2. ML-API (asynchron ausführen): Daten aus dem Speicher abrufen, ML-Funktionen ausführen und die Ergebnisse im Speicher speichern. Die Antwort wird jedoch zurückgegeben, bevor die Verarbeitung abgeschlossen ist.
  3. API herunterladen: Ergebnisse aus dem Speicher abrufen und zurückgeben

Jede API kann lose gekoppelt werden. Daher ist die Implementierung der Upload-API und der Download-API sehr flexibel. Es gibt verschiedene Möglichkeiten, es wie folgt zu verwenden.

Außerdem ist die Implementierung der Upload- und Download-APIs in anderen Sprachen als Python in Ordnung, und die APIs können sich auf verschiedenen Servern befinden, solange sie in denselben Speicher lesen und schreiben können. Sie können direkt vom Front-End aus in den Speicher lesen und schreiben, ohne die API zu durchlaufen. Insbesondere wenn es sich bei der Eingabe / Ausgabe um ein Bild handelt, ist es einfacher, den Cloud-Speicher direkt zu verwalten.

Vorteil

Nachteil

――Es ist schwierig zu verwenden, da es komplizierter ist als die Online-Vorhersage.

2. Implementierungsbeispiel

Lassen Sie uns die Online-Vorhersage- und Batch-Vorhersage-APIs mit der Fast-API implementieren. Wenn Sie sich das folgende Beispiel ansehen, werden Sie der Meinung sein, dass die Hürde, eine Inferenz-Pipeline zu einer Web-API zu machen, recht gering ist, wenn Sie die Inferenz-Pipeline lokal ordnungsgemäß funktionieren.

Was nicht zu tun

Dieser Artikel behandelt Folgendes nicht:

Was ist FastAPI?

Pythons Web-Framework, ein Mikroframework wie Flask. Zu seinen Stärken zählen hohe Leistung, einfache Schreibweise, ein Design mit starkem Fokus auf Produktionsabläufe und moderne Funktionen. Insbesondere die asynchrone Verarbeitung ist einfach zu handhaben.

Das Folgende basiert auf den Grundkenntnissen von Fast API. Wenn Sie die Details wissen möchten, lesen Sie bitte die folgenden Informationen.

Inferenz-API (gemeinsamer Teil)

Um vielseitig zu sein, definieren wir ein sehr grobes Modell. Es hat nichts zu bedeuten, ist aber einfach, daher nenne ich es eine Aufgabe der Emotionsanalyse für die Verarbeitung natürlicher Sprache.

Die erforderlichen Funktionen sind wie folgt. Wenn jedoch nur das Modell auf einen anderen Server ausgeschnitten ist, müssen Last und Modell nicht beibehalten werden.

Dieses Mal werden wir ein Modell verwenden, das zufällige Emotionen mit Vorhersage zurückgibt. Ich möchte die Verarbeitungszeit realisieren, also friere ich sie beim Laden für 20 Sekunden und bei der Vorhersage für 10 Sekunden ein.

ml.py


from random import choice
from time import sleep

class MockMLAPI:
    def __init__(self):
        # model instanse
        self.model = None

    def load(self, filepath=''):
        """
        when server is activated, load weight or use joblib or pickle for performance improvement.
        then, assign pretrained model instance to self.model.
        """
        sleep(20)
        pass

    def predict(self, x):
        """implement followings
        - Load data
        - Preprocess
        - Prediction using self.model
        - Post-process
        """
        sleep(10)
        preds = [choice(['happy', 'sad', 'angry']) for i in range(len(x))]
        out = [{'text': t.text, 'sentiment': s} for t, s in zip(x, preds)]
        return out

Anforderungs- / Antwortdatenformat

Definiert das Format der Anforderungsdaten. Versuchen wir, mehrere Eingaben zu unterstützen, wie unten gezeigt.

{
  "data": [
    {"text": "hogehoge"},
    {"text": "fugafuga"}
  ]
}

Die Antwortdaten sollten in einem Format vorliegen, das das Inferenzergebnis zur Eingabe hinzufügt und zurückgibt.

{
  "prediction": [
    {"text": "hogehoge", "sentiment": "angry"},
    {"text": "fugafuga", "sentiment": "sad"}
  ]
}

Definieren Sie das Schema wie folgt.

schemas.py


from pydantic import BaseModel
from typing import List

# request
class Text(BaseModel):
    text: str

class Data(BaseModel):
    data: List[Text]

# response
class Output(Text):
    sentiment: str

class Pred(BaseModel):
    prediction: List[Output]

2.1 Online-Prognose

Implementieren Sie eine Web-API für die Online-Vorhersage unter Verwendung der oben genannten allgemeinen Teile. Alles was Sie brauchen ist

--Laden Sie das trainierte Modell für maschinelles Lernen, wenn der Server gestartet wird

ist. Die minimale API wird durch Implementierung wie folgt vervollständigt.

main.py


from fastapi import FastAPI
from ml_api import schemas
from ml_api.ml import MockMLAPI

app = FastAPI()
ml = MockMLAPI()
ml.load() # load weight or model instanse using joblib or pickle

@app.post('/prediction/online', response_model=schemas.Pred)
async def online_prediction(data: schemas.Data):
    preds = ml.predict(data.data)
    return {"prediction": preds}

Funktionsprüfung

Überprüfen Sie den Betrieb lokal. Veröffentlichen Sie die Probeneingabe in CuRL. Anschließend können Sie bestätigen, dass die erwartete Ausgabe zurückgegeben wird. Da es 10 Sekunden gedauert hat, bis die Antwort zurückgegeben wurde, können Sie sehen, dass fast nur die vorhergesagte Verarbeitungszeit benötigt wurde.

$ curl -X POST "http://localhost:8000/prediction/online" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"data\":[{\"text\":\"hogehoge\"},{\"text\":\"fugafuga\"}]}" -w  "\nelapsed time: %{time_starttransfer} s\n"

{"prediction":[{"text":"hogehoge","sentiment":"angry"},{"text":"fugafuga","sentiment":"happy"}]}
elapsed time: 10.012029 s

2.1 Chargenvorhersage

Implementieren Sie eine Web-API für die Stapelvorhersage unter Verwendung der oben genannten allgemeinen Teile.

  1. Upload-API: Speichern Sie Daten für die Eingabe im Speicher (Datenbank, Cloud-Speicher usw.).
  2. ML-API (asynchron ausführen): Daten aus dem Speicher abrufen, ML-Funktionen ausführen und die Ergebnisse im Speicher speichern. Die Antwort wird jedoch zurückgegeben, bevor die Verarbeitung abgeschlossen ist.
  3. API herunterladen: Ergebnisse aus dem Speicher abrufen und zurückgeben

Input/Output Normalerweise sollten Sie die Daten im Cloud-Speicher oder in der Datenbank speichern. Der Einfachheit halber werden wir in diesem Artikel die Daten im CSV-Format im lokalen Speicher speichern. Definieren Sie zunächst eine Funktion zum Lesen und Schreiben. Beim Speichern der Eingabedaten wird ein Dateiname mit einer zufälligen Zeichenfolge erstellt, und eine Reihe von Stapelvorhersagen wird durchgeführt, indem die zufällige Zeichenfolge gegen eine API ausgetauscht wird. Die Implementierung mag lang erscheinen, aber in Wirklichkeit gibt es nur drei Dinge zu tun:

io.py


import os
import csv
from random import choice
import string
from typing import List
from ml_api import schemas

storage = os.path.join(os.path.dirname(__file__), 'local_storage')

def save_csv(data, filepath: str, fieldnames=None):
    with open(filepath, 'w') as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)

        writer.writeheader()
        for f in data:
            writer.writerow(f)

def load_csv(filepath: str):
    with open(filepath, 'r') as f:
        reader = csv.DictReader(f)
        out = list(reader)
    return out

def save_inputs(data: schemas.Data, length=8):
    letters = string.ascii_lowercase
    filename = ''.join(choice(letters) for i in range(length)) + '.csv'
    filepath = os.path.join(storage, 'inputs', filename)
    save_csv(data=data.dict()['data'], filepath=filepath, fieldnames=['text'])
    return filename

def load_inputs(filename: str):
    filepath = os.path.join(storage, 'inputs', filename)
    texts = load_csv(filepath=filepath)
    texts = [schemas.Text(**f) for f in texts]
    return texts

def save_outputs(preds: List[str], filename):
    filepath = os.path.join(storage, 'outputs', filename)
    save_csv(data=preds, filepath=filepath, fieldnames=['text', 'sentiment'])
    return filename

def load_outputs(filename: str):
    filepath = os.path.join(storage, 'outputs', filename)
    return load_csv(filepath=filepath)

def check_outputs(filename: str):
    filepath = os.path.join(storage, 'outputs', filename)
    return os.path.exists(filepath)

web API Erstellen Sie drei APIs: Upload, Inferenz und Download. Beachten Sie, dass die Stapelinferenz keine sofortige Antwort zurückgibt. Laden Sie das Modell daher jedes Mal, wenn die API aufgerufen wird.

Hier wird BackgourndTasks von FastAPI verwendet, um Modellinferenzen asynchron zu verarbeiten. Inferenz kann im Hintergrund verarbeitet werden und eine Antwort kann zuerst zurückgegeben werden, ohne auf das Ende zu warten.

main.py


from fastapi import FastAPI
from fastapi import BackgroundTasks
from fastapi import HTTPException
from ml_api import schemas, io
from ml_api.ml import MockBatchMLAPI

app = FastAPI()

@app.post('/upload')
async def upload(data: schemas.Data):
    filename = io.save_inputs(data)
    return {"filename": filename}

def batch_predict(filename: str):
    """batch predict method for background process"""
    ml = MockMLAPI()
    ml.load()
    data = io.load_inputs(filename)
    pred = ml.predict(data)
    io.save_outputs(pred, filename)
    print('finished prediction')

@app.get('/prediction/batch')
async def batch_prediction(filename: str, background_tasks: BackgroundTasks):
    if io.check_outputs(filename):
        raise HTTPException(status_code=404, detail="the result of prediction already exists")

    background_tasks.add_task(ml.batch_predict, filename)
    return {}

@app.get('/download', response_model=schemas.Pred)
async def download(filename: str):
    if not io.check_outputs(filename):
        raise HTTPException(status_code=404, detail="the result of prediction does not exist")

    preds = io.load_outputs(filename)
    return {"prediction": preds}

Funktionsprüfung

Überprüfen Sie den Vorgang auf die gleiche Weise wie bei der Online-Vorhersage. Veröffentlichen Sie die Probeneingabe in CuRL. Anschließend können Sie bestätigen, dass die erwartete Ausgabe zurückgegeben wird. Es wartet außerdem 30 Sekunden, bevor die Download-API aufgerufen wird. Sie können jedoch sehen, dass jede Antwort sehr schnell zurückgegeben wird.

$ curl -X POST "http://localhost:8000/upload" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"data\":[{\"text\":\"hogehoge\"},{\"text\":\"fugafuga\"}]}" -w  "\nelapsed time: %{time_starttransfer} s\n"
{"filename":"fdlelteb.csv"}
elapsed time: 0.010242 s

$ curl -X GET "http://localhost:8000/prediction/batch?filename=fdlelteb.csv" -w  "\nelapsed time: %{time_starttransfer} s\n"
{}
elapsed time: 0.007223 s

$ curl -X GET "http://localhost:8000/download?filename=fdlelteb.csv" -w  "\nelapsed time: %{time_starttransfer} s\n"   [12:58:27]
{"prediction":[{"text":"hogehoge","sentiment":"happy"},{"text":"fugafuga","sentiment":"sad"}]}
elapsed time: 0.008825 s

abschließend

Wir haben zwei typische Konfigurationen von Inferenz-Web-APIs für maschinelles Lernen eingeführt: Online-Vorhersage und Batch-Vorhersage. Es erfordert eine kleine Änderung der allgemeinen Web-API-Konfiguration, aber ich habe auch ein Implementierungsbeispiel vorgestellt, das einfach mit der Fast-API erstellt wird. Es wäre großartig, wenn Sie das Gefühl hätten, dass die Hürde, eine Web-API daraus zu machen, gering ist, wenn die Inferenz-Pipeline lokal ordnungsgemäß funktionalisiert ist. Die Aufregung des maschinellen Lernens ist endlos, aber ich habe das Gefühl, dass es immer noch wenig Informationen wie die Konfiguration der Web-API gibt ~~ (Es scheint ziemlich wahrscheinlich. Ich habe eine Sammlung von Links hinzugefügt). Ich denke, die in diesem Artikel vorgestellte Konfiguration ist auch grob. Ich würde mich freuen, wenn Sie Verbesserungen kommentieren könnten!

ähnliche Links

Dies ist ein Link, der in diesem Artikel nicht behandelt werden konnte.

Recommended Posts

Inferenz des maschinellen Lernmodells Web-API-Serverkonfiguration [Beispiel für eine schnelle API-Implementierung verfügbar]
Lernen eines neuronalen Netzes mit Chainer - Erstellen eines Web-API-Servers
Modell des maschinellen Lernens unter Berücksichtigung der Wartbarkeit