はじめに

機械学習モデルを作ったらそのまま放置することは私も経験があります。 ただそのままだとWebサイト上で活用(例:レコメンドシステム)として使うことはできなかったり、 定期的に動かすためにAPIを呼び出すなどができません。

そのため、Web API化して、サーバーとして起動できるようにしておくと システムに組み込んだりしやすくなりますので、 この記事ではできるだけ最小構成でFastAPIにMLモデルを組み込んでWebAPIとして呼び出せるWebサーバーを作りたいと思います。

FastAPIについて

APIを作るために特化しており高パフォーマンス(Fast)、実装時間がFastなOSSです。 FastAPI

2022/6/28時点でPythonの軽量webフレームワークとして有名なFlaskと比較すると、

watchfolkstar初回リリース
Flask2,15415,12759,3782010/4/16
FastAPI5893,64746,6512018/4/17

でした。 Flaskは2010年に最初のリリースがGithub上にあり、FastAPIは2018年にGithub上にリリースされました。 FastAPIは後発ということもありますので全体的にメトリックは小さいですが、それを鑑みてもStar数などは後発にも関わらず増えていることがわかります。

インストールや簡単な使い方は公式サイトに詳しく載っておりますのでそちらで実施していただければと思います 参考

今回使う機械学習モデル

Kaggleで有名なTitanicの機械学習モデルを使います。Codeは公開されているupuruさんのこちらのCodeを参考に使わせていただきます code:upura-kaggle-tutorial-03-feature-engineering

今回は最低限の構築にしたく、使わない特徴量はコード中で削除しています。 FastAPIで構築するときも使わない特徴量の削除は必要になりますので忘れなく削除する必要があります。

こちらのコードを参考にWebサーバーにモデルを取り込むため、pickleファイルを用意します。 下記はpickleファイルを作成するためのjupyterのコードです。

import numpy as np
import pandas as pd
import pickle
from sklearn.linear_model import LogisticRegression

train = pd.read_csv('./train.csv')
test = pd.read_csv('./test.csv')
combine = [train_df, test_df]

data = pd.concat([train, test], sort=False)

data['Sex'].replace(['male','female'], [0, 1], inplace=True)
data['Embarked'].fillna(('S'), inplace=True)
data['Embarked'] = data['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int)
data['Age'].fillna(999, inplace=True)

delete_columns = ['Name', 'PassengerId', 'SibSp', 'Parch', 'Ticket', 'Cabin', 'Fare']
data.drop(delete_columns, axis=1, inplace=True)

train = data[:len(train)]
test = data[len(train):]

y_train = train['Survived']
X_train = train.drop('Survived', axis=1)
X_test = test.drop('Survived', axis=1)

clf = LogisticRegression(penalty='l2', solver="sag", random_state=0)

clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

with open('model.pickle', mode='wb') as fp:
    pickle.dump(clf, fp)

model.pickleでモデルを出力をしています。

FastAPIで機械学習モデルをWeb API化

この作ったモデルを用いて、FastAPIでWebAPIのコードは下記になります。

from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
import pandas as pd
import pickle

app = FastAPI()

clf = pickle.load(open("model.pickle", "rb"))

class Predict(BaseModel):
    PassengerId: int
    Pclass: int
    Name: str
    Sex: str
    Age: int
    SibSp: int
    Parch: int
    Ticket: str
    Fare: float
    Cabin: str
    Embarked: str


def _titanic_predict(df):
    df['Sex'].replace(['male', 'female'], [0, 1], inplace=True)
    df['Age'].fillna(999, inplace=True)
    df['Embarked'].fillna(('S'), inplace=True)
    df['Embarked'] = df['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int)
    delete_columns = ['Name', 'PassengerId', 'SibSp',
                      'Parch', 'Ticket', 'Cabin', 'Fare']
    df.drop(delete_columns, axis=1, inplace=True)

    return clf.predict_proba(df)

@app.post("/titanic/")
def titanic(req: Predict):
    df = pd.DataFrame([req.dict()])
    prediction = _titanic_predict(df)
    return {"res": "ok", "proba of survive": prediction[0][1]}

clf = pickle.load(open("model.pickle", "rb"))

でモデルをロードしています。 さきほど実施した特徴量の削除も忘れなく実施しています(delete_columns = …)。

uvicorn main:app --reload

上記コマンドで実行します。 テストとしてcurlでもリクエストを遅れますし、FastAPIのweb ui上からrequestのテストができます。 web ui上からテストする場合は下記のような図でテストできます。 URLは環境によって異なりますが、私が実施した環境では下記のlocalhostを指定して参照することができました。

FastAPIのテスト画面

http://127.0.0.1:8000/docs

おわりに

最小構成でFastAPIを使って機械学習モデルをWeb API化をしてみました。 もちろん実際の検証ではサーバーにデプロイして、Public IPを付与して外部公開したり、 もしきはバックエンドサーバーからしかアクセスできないように認証を設けたり、 外部からアクセスできないネットワークでのサーバーをデプロイしたりと 本番環境で実施するためには他にもやることはありますが上記だけでモデルをサービングする機能が対応しております。 Flaskなどで同様な機能をつくるとコード量が増えたり、Web UIの機能がなかったりしますので FastAPIはWeb APIを作る際に使いやすいツールかと思いますので良かったら試してみてはいかがでしょうか。

参考

FastAPIを使って 機械学習モデルをapi化してみた