盆暗の学習記録

データサイエンス ,エンジニアリング,ビジネスについて日々学んだことの備忘録としていく予定です。初心者であり独学なので内容には誤りが含まれる可能性が大いにあります。

機械学習モデルを動かすWeb APIを作ってみる(4):chaliceでLambdaにデプロイ

これまで,APIの作成からHerokuへのデプロイまでを扱ってきました。 今回はAWS Lambda + API Gatewayでのデプロイについてメモしておきます。


f:id:nigimitama:20200223205516p:plain

今回はchaliceを使ってAWS Lambda + API Gatewayでデプロイしていきます。

chaliceAWS謹製のライブラリで,Flaskライクな文法でWebアプリを作ることができ,コマンド一つでLambda + API Gatewayの構成でデプロイすることができます。

chaliceのセットアップ

インストール

pip3 install chalice

プロジェクトの作成

chalice new-project <project_name>

プロジェクトを作成すると,app.pyrequirements.txtの入ったディレクトリが作られるので,そちらに移動してapp.pyを書き換えていきます。

app.pyの書き換え

最初はこんな感じになってます。

from chalice import Chalice

app = Chalice(app_name='project_name')

@app.route('/')
def index():
    return {'hello': 'world'}

これを以下のように書き換えます。

from chalice import Chalice, Response
import pandas as pd
import pickle
from datetime import datetime
import sys
import json
sys.path.append("./modules")  # 前処理で使った自作モジュール「pipeline」を読み込むためPYTHONPATHに追加
app = Chalice(app_name='with_chalice')


# アプリ起動時に前処理パイプラインと予測モデルを読み込んでおく
preprocess = pickle.load(open("modules/preprocess.pkl", "rb"))
model = pickle.load(open("modules/model.pkl", "rb"))


@app.route('/predict', methods=["POST"])
def predict():
    """/predict にPOSTリクエストされたら予測値を返す関数"""
    try:
        # APIにJSON形式で送信された特徴量
        request = app.current_request
        X = pd.DataFrame(request.json_body, index=[0])
        # 特徴量を追加
        X["trade_date"] = datetime.now()
        # 前処理
        X = preprocess.transform(X)
        # 予測
        y_pred = model.predict(X, num_iteration=model.best_iteration_)
        response = {"status": "OK", "predicted": y_pred[0]}
        return Response(body=json.dumps(response),
                        headers={'Content-Type': 'application/json'},
                        status_code=200)
    except Exception as e:
        print(e)  # デバッグ用
        response = {"status": "Error", "message": "Invalid Parameters"}
        return Response(body=json.dumps(response),
                        headers={'Content-Type': 'application/json'},
                        status_code=400)

requirements.txtは空のままで

機械学習モデル部分で使用しているライブラリを載せるんですが,Lambda関数に含めることができるのは50MBまでなので,LightGBM(とその依存ライブラリ)だけで超過します。

なのでライブラリは後述するLayerという仕組みを使って別のルートからアップロードすることにし,requirements.txtはそのままにします。

ローカルでテスト

chalice local

localhost:8000にサーバーが起動するのでPOSTリクエストを投げてテストします。

(存在しないURLにリクエストを投げたときにNot Foundじゃなく{"message":"Missing Authentication Token"}が返ってくるというややミスリーディングな仕様なのでご注意を)

$ python3 api_test.py 
{"status": "OK", "predicted": 45833222.1903707}
.
----------------------------------------------------------------------
Ran 1 test in 0.064s

OK

ローカルで問題なく動くようであればデプロイしていきます。

AWS Credentials

deployの前にAWSの資格情報を編集します。

アクセスキーはAWSコンソールのセキュリティ資格情報のところで作成します。

$mkdir ~/.aws
$ cat >> ~/.aws/config
[default]
aws_access_key_id=YOUR_ACCESS_KEY_HERE
aws_secret_access_key=YOUR_SECRET_ACCESS_KEY
region=YOUR_REGION (such as us-west-2, us-west-1, etc)

LambdaのLayerにパッケージを追加する

LightGBM, scikit-learnなどの外部ライブラリをrequirements.txtに記述してchaliceのデプロイ時にアップロードさせる方法(直接アップロード)では,圧縮済みファイルで50MBが上限となります。

Numpy, Scipy, Pandas, LightGBM, Scikit-learnなどを使っている今回のプロジェクトは50MB以下に収まらないので,直接アップロードはできません。

しかし,ここを見ると,直接アップロードなら50MBが上限でも,Layerから読み込む方法なら解凍時で250MBまでは使えるようです。

なので,lambda関数にまとめるのではなく,Layerとして別口でアップロードしておき,Lambda関数から読み込ませるようにします。

以下の記事を参考にしてLayerを作っていきます。

Amazon Linux2 OSのEC2インスタンスを立ち上げ,sshで接続します

pip installしてzipにする

# python3のインストール
sudo yum update -y
sudo yum install python3-devel python3-libs python3-setuptools -y
sudo python3 -m pip install --upgrade pip

# numpyのzip化 -------------
mkdir python
pip3 install -t ./python/ numpy
zip -r numpy.zip python
rm -r python

# scipyのzip化 -------------
mkdir python
pip3 install -t ./python/ scipy
# numpyは別Layerにするので消す
rm -r ./python/numpy*
zip -r scipy.zip python
rm -r python

# pandasのzip化 ------------
mkdir python
pip3 install -t ./python/ pandas==0.25.3
# numpyは別Layerにするので消す
rm -r ./python/numpy*
zip -r pandas.zip python
rm -r python

# sklearnのzip化 -----------
mkdir python
pip3 install -t ./python/ sklearn
# numpy, scipyは別Layerにするので消す
rm -r ./python/numpy* ./python/scipy*
zip -r sklearn.zip python
rm -r python

# lightgbmのzip化 ----------
mkdir python
pip3 install -t ./python/ --no-deps lightgbm
zip -r lightgbm.zip python
rm -r python

zipをダウンロード

exit

でEC2を抜けたら

scp -i ~/.ssh/[pemファイル名].pem ec2-user@[IPv4]:~/*.zip .

のような感じでローカルにダウンロードします。

zipをアップロード

  • AWSコンソール→Lambda→Layer→「レイヤーの作成」からzipをアップロードします

    • レイヤー名はpythonコードでimportするときの名前にします

Layerの追加

AWSコンソールのLambda関数の画面から,作成中のAPIの関数に先程作成したLayerを追加します。

ソースコード以外のデプロイしたいファイルはvendorに入れる

chaliceでdeployしたいファイルが有る場合はvendorというディレクトリを作って入れる必要があります(Chalice documentation)。

今回は予測モデルのpklファイルを入れていたmodulesというディレクトリをvendorに入れます

mkdir vendor
cp -r modules/ vendor/

デプロイする

ようやくデプロイです!

chalice deploy

動作確認

LambdaのURLにPOSTリクエストを投げて確認します。

$ python3 api_test.py
{"status": "OK", "predicted": 45833222.1903707}
.
----------------------------------------------------------------------
Ran 1 test in 3.051s

OK

ちゃんと動作しているようです。

Githubリポジトリ

今回のファイル構成などはこちらになります。

github.com