機械学習モデルを利用したAPIを作成しようと思い、
Lambdaでpandas
とscikit-learn
を使おうとしてみたら思いのほかハマった。
今回のハマりポイント
- lambdaのデプロイパッケージ容量制限(ZIPで50MB)
- Amazon Linuxの環境依存
- pythonでのモジュールのインポート
lambdaのデプロイパッケージ容量制限(ZIPで50MB)
まず問題になったのはデプロイパッケージ容量だ。
pandas
とscikit-learn
と依存ライブラリを含めると、zipで圧縮しても60MBほどあって、制限を超えてしまう...
そこで、/tmp フォルダ を使う
lambdaではユーザーが使用できる容量512MBの/tmp
が割り当てられている。
つまり、デプロイパッケージに入りきらないライブラリを
実行時にs3からダウンロードして/tmp
に展開してパスを通した後インポートすればいい。
今回やりたいのはコレだけだけど、付随する問題がいろいろ出てきて死んだ。
Amazon Linuxの環境依存
まず、容量制限に引っかからないpandas
と依存ライブラリを含めたデプロイパッケージ(30MBほど)を作成し動作確認してみた。
なんかエラー出る...
Unable to import module 'main': Missing required dependencies ['numpy']
numpy
はパッケージに含めているのに、、なんで?
調べてみるとこんな記事が見つかった。
Amazon Linuxと同じオプションでビルドされたPythonを使って安全にLambda functionをデプロイしよう
つまりlambdaで正常に動作させるためには、
lambda実行環境である Amazon Linux でデプロイパッケージを作成する必要がある、ということ。
めんどいかよ
docker
環境、以前ec2で使ってたのがあるけど、
アクセスキー紛失してアクセスできなくなったんだよ(え?)
インスタンス作り直すのめんどいなー、と思ってggってたら、
すばらしい人がすでにAmazon Linux
でビルドしたパッケージを公開してくれてた。
https://github.com/chennavarri/aws-lambda-pandas-sample
https://github.com/ryansb/sklearn-build-lambda
...だめでした。
python
のバージョンがそれぞれ違った。
しかたないのでec2でAmazon Linux
環境作ってパッケージ作った。
これを使うとlambda環境まで再現できる。
https://github.com/lambci/docker-lambda
pythonでのモジュールのインポート
ようやくデプロイパッケージができたのでlambdaで実行してみる。
pandas
はデプロイパッケージに含めて、sklearn
は/tmp
以下に展開することにした。
コードは単純で、
s3からsklearn
のzipファイルを/tmp
にダウンロードし、展開するというもの。
import boto3
import os
import sys
import zipfile
import pandas as pd
from pprint import pprint
s3 = boto3.resource('s3')
bucket = s3.Bucket('data')
def main():
load_sklearn()
pprint(os.listdir("/tmp/site-packages/"))
import sklearn
return "done"
def load_sklearn():
bucket.download_file("sklearn-packages.zip", '/tmp/sklearn-packages.zip')
zip_ref = zipfile.ZipFile('/tmp/sklearn-packages.zip', 'r')
zip_ref.extractall('/tmp')
zip_ref.close()
os.remove("/tmp/sklearn-packages.zip")
def handle(event, context):
return main()
実行結果 (抜粋)
['bin',
'scipy-1.1.0.dist-info',
'sklearn',
'scipy',
'scikit_learn-0.19.1.dist-info']
No module named 'sklearn': ModuleNotFoundError
sklearn
はちゃんとダウンロードされて展開されているのに、
モジュールが見つからないというエラー...
どうやらlambdaの環境変数でPYTHONPATH
に/tmp/site-packages
を追加していたのが原因らしい。
PYTHONPATH
から/tmp/site-packages
を消し、モジュール追加後にモジュールパスを追加するようにすると正常に動作した。
def main():
load_sklearn()
pprint(os.listdir("/tmp/site-packages/"))
sys.path.append('/tmp/site-packages') # ←これを追加
import sklearn
return "done"
実行結果
"done"
あらかじめ環境変数で指定したモジュールパスについては、実行の初めに探索されて、以降に追加されたモジュールは検知しない、とかなのかな?
import
文のタイミングで探索すると思ってたから、最初は詰んだかと思った。
詳しい方はコメントぜひ!
まとめ
cold start
だと数秒かかるので実用は厳しい
おすすめを教えてほしい