概要
turicreateという Apple製の機械学習のライブラリがある。(machine learning modelsってことなのでそれで良いはず)
こいつを使ったモデルをGCP上で扱う構成を作ったときのメモ
参考: https://cloud.google.com/blog/products/ai-machine-learning/how-to-serve-deep-learning-models-using-tensorflow-2-0-with-cloud-functions
構成
*CloudRunって書いてある部分はアイコンがなかったのでGAEのアイコンで代用しています
ユーザはGAEに対してアクセスし、その後ろでCloud Runがstorageからmodelをdownloadしてきて動く。
もちろん毎回storageから持ってくると意味がないのでロードしたモデルはメモリ上にキャッシュする。
困ったこと+解消方法
参考の構成では、TensorflowのmodelをCloud FunctionsでloadしてAPIとして、返しているがTuricreateの場合、おそらく依存関係の解消をしようとしている際にbuildができずにエラーになってしまう。(GAEでやっても同じ、Cloud Buildの設定をいじれれば良いのかもしれないがGAEやCloud Functionsからそれが触れるのかがわからなかった)
そのため、自分で実行imageを作ってdeployし動かすことができる、Cloud Runを使うことにした
実装
実装自体は何ということのないもの、ほぼほぼpython+CloudRunの構成に沿っている
import turicreate
import os
from flask import Flask, request, jsonify
from google.cloud import storage
import zipfile
model = None
app = Flask(__name__)
def download_model(bucket_name, source_blob_name, dest_blob_name):
storage_client = storage.Client()
bucket = storage_client.get_bucket(bucket_name)
blob = bucket.blob(source_blob_name)
blob.download_to_filename(dest_blob_name)
with zipfile.ZipFile(dest_blob_name) as modelZip:
modelZip.extractall('.')
@app.route('/')
def root():
global model
request_json = request.get_json()
if request.args and 'userId' in request.args:
userId = request.args.get('userId')
else:
return jsonify({'message': 'userId is not found'}), 400
if 'limit' in request.args:
limit = int(request.args.get('limit'))
else:
limit = 10
if model is None:
load_model()
result = model.recommend(users=[userId], k=limit)
random.shuffle(result)
return jsonify({'result': result})
def load_model():
global model
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = './credential.json'
download_model("learning-data", "model.zip", "./model.zip")
model = turicreate.load_model('./model')
if __name__ == '__main__':
app.run(host='127.0.0.1', port=int(
os.environ.get('PORT', 8080)), debug=False)
FROM python:3.7
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . .
RUN pip install -r requirements.txt
CMD [ "python", "main.py" ]
turicreate==6.1
flask==1.1.1
gunicorn==20.0.4
google-cloud-storage==1.26.0
GAEの方は出来上がったserviceにアクセスしていればOK
課題
- 上記の場合instanceが残り続けた場合どうなるのか、modelの更新のタイミングをみて動くようにしたい
- instanceが増えた場合どうやって更新するか
→ 一旦考えているのは、メモリにあるデータは key: 時間、 value: model
のようにすることで別途タイミングで先にモデルデータを非同期で読み込むようにしておく方法
このあたりが参考になるかも
https://cloud.google.com/run/docs/tips?hl=ja