TensorFlowモデルをAmazon Linux 2環境にデプロイしてFlaskでWeb API化

手元のGPU環境で作成したTensorFlowモデルを、クラウド環境上で動作させたいということは良くあると思います。実際、いま私もそのようなお仕事をしているのですが・・・。
クラウド上で動作させるといっても、方法はいろいろあります。Amazon SageMakerのようなマネージドなクラウドを使用してデプロイすればWeb API化まで簡単に行うことができますし、一方でAmazon EC2のようなIaaSを用いて自分ですべて環境構築するという方法もあります。ちなみに、モデルをGPU環境で作って、それをEC2のようなCPUのみの環境で動作させるということも、もちろん可能です。(ちょっとワーニングが出ますけど。)

構成

EC2環境でTensorFlowモデルをデプロイする方法として、以下の2つが考えられます。

  • TensorFlow Servingを使用する
  • TensorFlowでモデルを動かし予測処理を行い、Flaskアプリを作ってWeb API化する

前者のTensorFlow Servingは、拙著「使ってわかったAWSのAI」で簡単な使い方を説明しました。きちんと使えば、前処理などの処理もパイプライン的に行えるので良さそうです。
ただ、今回は後者の方法を用いることにしました。

Nginx → uWSGI → Flask → TensorFlow

こんな構成です。

環境構築

まず、Amazon Linux 2環境のEC2インスタンスを準備します。インスタンスタイプはひとまずt2.mediumを選択しました。同時処理数などにもよりますが、この程度の性能の環境でも推論処理は問題なく動作します。

手元の環境がPython 3.8だったので、Amazon Linux 2環境も同様のバージョンにします。
どうやら、バージョンを合わせないとh5ファイル(TensorFlowモデルを保存したファイル形式)の読み込みでエラーになるようです。

sudo amazon-linux2-extras install epel
sudo amazon-linux2-extras install nginx1
sudo amazon-linux2-extras install python3.8
sudo yum install python38-devel
sudo yum groupinstall "Development Tools"
yum install pcre pcre-devel

Python3.8のpipを使用可能にします。

sudo pip3.8 install --upgrade pip
sudo ln -s /usr/local/bin/pip3.8 /usr/bin/pip3.8

requirements.txtを下記のようにします。

uwsgi
Flask
tensorflow==2.5.0
# 以下は必要に応じて
awscli
boto3
boto
pycryptodome

あとは、このrequirements.txtを用いてPythonパッケージをインストールします。

sudo pip3.8 install -r requirements.txt

Flaskアプリの構築

/home/ec2-user/app/app.pyというファイルを作成し、下記の内容にします。
モデルを保存したh5ファイルは、/home/ec2-user/app/models/model.h5に保存しました。
label_mapやpreprocessのあたりは、作成したモデルやトレーニング時の設定に合わせます。

#!flask/bin/python
import json
from flask import Flask, Response, request
import base64
import io
import os
import numpy as np
import tensorflow as tf
from datetime import datetime as dt

app = Flask(__name__)

image_dir = '.'
now = dt.now()

model = tf.keras.models.load_model(os.path.join('models', 'model.h5'))
label_map = {'label0': 0, 'label1': 1, 'label2': 2}


@app.route('/predict', methods=['POST'])
def predict():
    json_data = request.get_json()
    image_base64 = json_data['image']
    image_file = os.path.join(image_dir, '%s.JPG' % now.strftime('%Y%m%d%H%M%S'))
    with open(image_file, 'wb') as f:
        f.write(base64.b64decode(image_base64))

    img = tf.keras.preprocessing.image.load_img(
        image_path, target_size=(224, 224), color_mode='rgb')
    x = tf.keras.preprocessing.image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = tf.keras.applications.inception_resnet_v2.preprocess_input(x)
    preds = model.predict(x)

    result = None
    for pred, value in label_map.items():
        if value == np.argmax(preds):
            result = pred
            print('Predicted class is:', pred,
                  'With a confidence score of: ', np.max(preds))

    return Response(json.dumps({'result': result}), mimetype='application/json', status=200)


if __name__ == '__main__':
    app.run()

uWSGI

/home/ec2-user/app/app.iniというファイルを作成し、下記の内容にします。
ログの保存場所として、/home/ec2-user/logsというフォルダを作成しておきます。

[uwsgi]
module = app
callable = app
die-on-term = true
chdir = /home/ec2-user/app
processes = 1
master = false
vacuum = true
socket = /run/uwsgi.sock
chmod-socket = 666

logto = /home/ec2-user/logs/uwsgi.log

uWSGIを起動するため、/etc/systemd/system/uwsgi.serviceを作成し、下記の内容にします。

[Unit]
Description = uWSGI
After = syslog.target

[Service]
ExecStart = /usr/local/bin/uwsgi --ini /home/ec2-user/app/app.ini
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

あとは、下記のとおり起動します。

sudo systemctl enable uwsgi
sudo systemctl start uwsgi

Nginx

Nginxを導入している場合、/etc/nginx/conf.d/flask.confのようなファイルを作成し、下記のようにuWSGIへのリバースプロキシを設定します。

server {
    server_name abc.vivinko.com; # 例
    location / {
        include uwsgi_params;
        uwsgi_pass unix:///run/uwsgi.sock;
    }
}

OpenCV

OpenCVを使用する場合は、下記のようにインストールするか、上記のrequirements.txtに書いてまとめてインストールします。

sudo pip3 install opencv-python

import cv2ImportError: libGL.so.1: cannot open shared object file: No such file or directoryというエラーが出る場合は、下記のパッケージをインストールします。

sudo yum install mesa-libGL.x86_64

参考サイト

  • https://qiita.com/meriam_dev/items/2fe225cd46bbae3a0dc2

この記事を書いた人

井上 研一

株式会社ビビンコ代表取締役、ITエンジニア/経済産業省推進資格ITコーディネータ。AI・IoTに強いITコーディネータとして活動。2018年、株式会社ビビンコを北九州市に創業。IoTソリューションの開発・導入や、画像認識モデルを活用したアプリの開発などを行っている。近著に「使ってわかった AWSのAI」、「ワトソンで体感する人工知能」。日本全国でセミナー・研修講師としての登壇も多数。