ディープラーニングを使ったウェブアプリケーションをすばやく作る

こんにちは。データサイエンスチームのtmtkです。
NHN テコラス Advent Calendar 2018の1日目の記事です。
この記事では、FlaskとKerasを使ってディープラーニングのウェブアプリケーションをすばやく作る方法を紹介します。

なぜデータサイエンティストがウェブアプリケーションを作るのか

当データサイエンスチームには、社内外の企業が蓄積しているデータに対して機械学習を適用し、価値を生み出すという業務があります。たとえば、コムニコ様の事例では、コムニコ様のお持ちの画像データを活用し、Instagramでの「いいね!」の数を予測する機械学習モデルを作成しました。また最近では、社内のデータに対し機械学習を活用するプロジェクトが進んでいます。
データを収集して機械学習モデルを作るというのは、データサイエンティストや機械学習エンジニアの重要な役割です。しかし、せっかく苦労して機械学習モデルを作っても、それを使ってもらえなければ意味がありません。「Kerasで読み込める機械学習モデルを作りました。パラメータチューニングをがんばった結果、Accuracyが99%も出ました。モデルを s3://some-shared-bucket/model.h5 に保存しておくので、あとはよろしくお願いします。」といってデータサイエンティストとしての自分の仕事を終えられるでしょうか? モデルファイルだけを渡しても、対応を後回しにされるか、悪い場合にはそのまま忘れ去られてしまうかもしれません。機械学習用のライブラリのモデルファイルの扱い方なんて多くの人は知らないのだし、誰しも自分の仕事で忙しいものです。
そんなとき、機械学習の結果を簡単に使えるウェブアプリケーションの試作品を用意しておけば、気軽に使ってみてもらうことができます。一度動くものを触ってもらえれば、有効性を確認してもらえます。有効性がわかってもらえれば、活用のために時間を割いてくれるかもしれません。
プロトタイプを作成することの重要性はさまざまなところで指摘されています。「Windows 95/98の生みの親」とも言われる中島聡氏にも指摘されていますし、リーン・スタートアップの文脈で語られるMinimum Viable Productも似た考えだと思います。また、任天堂の名作ゲーム「Splatoon(スプラトゥーン)」の企画・開発にあたっても、実際に遊べる試作があったことが採用の決め手になったということが述べられています。

(Splatoonの元になった“豆腐のゲーム”。社長が訊く『Splatoon(スプラトゥーン)』|Wii U|Nintendoより)

岩田

70個ものアイデアのなかから
豆腐のゲームが選ばれる決め手になったのは、
なんだったんですか?

天野

やっぱり、実際に遊べる試作があった、
というのがすごく大きかったと思います。

野上

試作の時点で4人対4人のネット対戦が
できるようになっていたんです。

岩田

最初から4人対4人ができたんですか・・・。
プログラマーはやっぱり強いですね(笑)。

佐藤

(うれしそうにうなずく)

岩田

新しい構造のものをつくるときは、
“プログラマー最強説”というのがあるんですよね。

佐藤

そうですよね(笑)。

岩田

わたしもプログラマー出身ですから
この手をよく使いました(笑)。

一同

(笑)

社長が訊く『Splatoon(スプラトゥーン)』|Wii U|Nintendoより)

機械学習モデルを作って終わるのではなく、実際に動くデモやプロトタイプ・アプリケーションを作ることで、実際の活用に近づけたり説得力を持たせたりすることができます。そのため、データサイエンティストが機械学習モデルを手早くウェブアプリケーションにできることは重要です。

作るもの

今回は、KerasFlaskをつかってディープラーニングを使ったシンプルなウェブアプリケーションを作ります。作るものは、

このシンプルな画面で

画像ファイルを選択してsubmitボタンを押すと

シンプルな結果画面が表示されるというものです。
KerasとFlaskを使うかわりにTensorFlow.jsのようなライブラリを使ってクライアント側に推論処理をさせることもできますが、その場合はモデルをクライアントにダウンロードさせる必要があり、モデルを秘匿できません。また、モデルのダウンロードと推論処理に時間がかかるという欠点もあります。今回はサーバサイドで推論を完結させるため、KerasとFlaskを組み合わせたアーキテクチャにします。
品質としては、全世界に公開してサービスとして使ってもらえる水準ではなく、限られた人がアクセスして検証に使えるというものを想定して作ります。したがって、エラー処理やセキュリティ対策についてはいったん考慮しません。

実際に作る

FlaskのドキュメントのUploading Files — Flask 1.0.2 documentationとKerasのドキュメントのApplications – Keras Documentationを参考にして、簡単なプログラムを書きます。
次のソースコードを、 app.py として保存します。

""" Simple web application using Keras """

import tensorflow as tf
from keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input, decode_predictions
from keras.preprocessing import image
from flask import Flask, request
import numpy as np

app = Flask(__name__)
graph = tf.get_default_graph()
model = MobileNetV2()

def preprocess_image(image_file):
    """ Load and preprocess an image file for the model """
    img = image.load_img(image_file, target_size=(224, 224))
    x_img = image.img_to_array(img)
    x_img = np.expand_dims(x_img, axis=0)
    x_img = preprocess_input(x_img)
    return x_img

def format_prediction(preds):
    """ Format predictions """
    ret = "<html><body>{}</body></html>"
    ret = ret.format(
        "".
        join([
            "

{}: {}%

".format(name, int(prob * 100))
            for _, name, prob
            in decode_predictions(preds, top=3)[0]
        ])
    )
    return ret


@app.route("/", methods=["GET", "POST"])
def index():
    """ Index page. """
    if request.method == "GET":
        # HTML page with file form
        ret = """
<html>
  <body>
    
<form method="post" enctype="multipart/form-data">
      <input type="file" name="file">
      <input type="submit" value="submit">
    </form>

  </body>
</html>
"""
    elif request.method == "POST":
        file = request.files["file"]
        x_img = preprocess_image(file)

        # Do inference
        with graph.as_default():
            preds = model.predict(x_img)

        ret = format_prediction(preds)

    return ret

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8888)


この100行足らずのソースコードで、画像を分類するウェブアプリケーションを作ることができました。
このソースコードで解説を要する点として、10行目のgraph = tf.get_default_graph()と、55行目のwith graph.as_default():があります。この処理を入れないとmodel.predict()が例外を吐いて失敗します。原因について筆者はあまり理解していないのですが、マルチスレッド処理が関係しているようです。詳しくはFlask + Tensorflow + Keras で モデルからのpredictが動作しない問題 – DEV Community 👩‍💻👨‍💻Tensorflow backend – bug in model._make_predict_function(…) · Issue #2397 · keras-team/keras · GitHubで議論されています。
Pythonの処理系や必要なライブラリをインストールして、起動します。ここでは説明を簡単にするため、Dockerで説明します(Dockerに抵抗がある場合は、Dockerを使わずにPythonとその各種ライブラリをインストールして環境構築をしても構いません)。次のテキストをDockerfileという名前で保存します。

FROM python:3.6
EXPOSE 8888
RUN pip install tensorflow==1.12.0 Keras==2.2.4 Flask==1.0.2 Pillow==5.3.0
COPY app.py /app/app.py
CMD python /app/app.py

最後に、Dockerをつかって起動します。DockerをインストールしてあるLinux環境で、次を実行します。

docker build -t simple_webapp .
docker run -p 8888:8888 --name simple_webapp simple_webapp

すると、Dockerがコンテナイメージの作成と実行を行います。MobileNetV2のモデルがダウンロードされ、ウェブアプリケーションが立ち上がります。その状態で http://localhost:8888/ にアクセスすれば、先ほどのシンプルなアプリケーションが使えます。
自分でつくったモデルを使うには、app.pymodel = MobileNetV2()の部分をmodel = keras.models.load_model("mymodel.h5")などに変更し、preprocess_image()format_prediction() を適宜書き換えればいいです。

注意点

前に述べたように、検証用の水準なので、注意点がいくつかあります。まず、Flaskに備え付けられているウェブサーバを使っていますが、このウェブサーバはFlaskの公式ページで本番環境では使うべきでないとされています。そのため、本番環境ではGunicornなど別のウェブサーバと組み合わせる必要があります。他にも、画像でないファイルがアップロードされた場合の例外処理が入っていません。負荷分散や障害対策についても考えていません。
そのため、IPアドレスでアクセスを制限するなどして、限られた人しかこのウェブアプリケーションにアクセスできないようにする必要があります。

まとめ

この記事では、プロトタイピングの重要性について述べ、Kerasで作ったモデルを利用するウェブアプリケーションのプロトタイプを作成しました。

参考文献

AWS移行支援キャンペーン

あなたにおすすめの記事