これまで,APIの作成からHerokuへのデプロイまでを扱ってきました。 今回はAWS Lambda + API Gatewayでのデプロイについてメモしておきます。
- 機械学習モデルを動かすWeb APIを作ってみる(1):APIの作成 - 盆暗の学習記録
- 機械学習モデルを動かすWeb APIを作ってみる(2):uWSGIの設定 - 盆暗の学習記録
- 機械学習モデルを動かすWeb APIを作ってみる(3):Herokuにデプロイ - 盆暗の学習記録
今回はchaliceを使ってAWS Lambda + API Gatewayでデプロイしていきます。
chaliceはAWS謹製のライブラリで,Flaskライクな文法でWebアプリを作ることができ,コマンド一つでLambda + API Gatewayの構成でデプロイすることができます。
- chaliceのセットアップ
- app.pyの書き換え
- requirements.txtは空のままで
- ローカルでテスト
- AWS Credentials
- LambdaのLayerにパッケージを追加する
- ソースコード以外のデプロイしたいファイルはvendorに入れる
- デプロイする
- 動作確認
- Githubリポジトリ
chaliceのセットアップ
インストール
pip3 install chalice
プロジェクトの作成
chalice new-project <project_name>
プロジェクトを作成すると,app.py
とrequirements.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で接続します
- コンパイラ言語を利用しているライブラリはLambda(2019年からAmazon Linux2で動いている)と同じOSでインストールしたライブラリを使う必要があるようです (Python3.7ランタイムのAWS LambdaでC拡張ライブラリを使用したい! - Qiita)
- コンパイラ言語を使用していないライブラリ(例:tqdm)ならEC2じゃない場所で以下の2~4の作業を行えば大丈夫です。
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をアップロード
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リポジトリ
今回のファイル構成などはこちらになります。