質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • Python 3.x

    10255questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Keras

    508questions

  • Flask

    365questions

    FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

  • Azure

    271questions

    Azureは、マイクロソフトのクラウド プラットフォームで、旧称は Windows Azureです。PaaSとIaaSを組み合わせることで、 コンピューティング・ストレージ・データ・ネットワーキング・アプリケーションなど多くの機能を持ちます。

  • IIS

    213questions

    IIS(Internet Information Services)はマイクロソフト社によって開発されたwebサーバーです。Windows上で動作します。

Azure WebApps にデプロイしたPython機械学習プログラムがタイムアウトする

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 760

ShikaTech

score 432

 前提

Keras+Tensorflowで画像のカテゴリ分類を行なうモデルを作成しました。
これをPythonのFlaskライブラリを使って公開したAPIで読み込み、APIにPOSTされた画像のカテゴリを判定する、というプログラムを作り、AzureのWebAppsに配置します。

 発生している問題

モデルが1つの時はデプロイ後正しいURLを叩くことでWEBアプリが起動できていたんですが、モデルが多くなるにつれてタイムアウトエラーが発生してしまうようになりました。

HTTP Error 500.0 - Internal Server Error
D:\home\python364x64\python.exe - The FastCGI process exceeded configured activity timeout

これを解決する案、設定、そのほか何かお知恵はありませんでしょうか?

 サンプルコード(main.py)

import base64
import io
import json
import time
from datetime import datetime
from platform import python_version

import keras
import numpy as np
from flask import Flask, jsonify, make_response, request
from keras import metrics
from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D
from keras.models import Sequential, load_model
from keras.preprocessing.image import array_to_img, img_to_array, load_img
from keras.utils import np_utils
from PIL import Image

start = time.time()
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":App start.")


app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":1st model load start.")
s1model = load_model("./models/category1.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":1st model loaded. Next 2.")
s2model = load_model("./models/category2.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":2nd model loaded. Next 3.")
s3model = load_model("./models/category3.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":3rd model loaded. Next 4.")
s4model = load_model("./models/category4.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":4th model loaded. Next 5.")
s5_P1model = load_model("./models/category5_P1.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":5th model loaded. Next 6.")
s6_P1model = load_model("./models/category6_P1.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":6th model loaded. Next 7.")
s7_P1model = load_model("./models/category7_P1.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":7th model loaded. Next 8.")
s5_P2model = load_model("./models/category5_P2.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":8th model loaded. Next 9.")
s6_P2model = load_model("./models/category6_P2.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":9th model loaded. Next 10.")
s7_P2model = load_model("./models/category7_P2.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":10th model loaded. Next 11.")
s5_P3model = load_model("./models/category5_P3.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":11th model loaded. Next 12.")
s6_P3model = load_model("./models/category6_P3.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":12th model loaded. Next 13.")
s7_P3model = load_model("./models/category7_P3.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":13th model loaded. Next 14.")
s5_P4model = load_model("./models/category5_P4.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":14th model loaded. Next 15.")
s6_P4model = load_model("./models/category6_P4.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":15th model loaded. Next 16.")
s7_P4model = load_model("./models/category7_P4.hdf5")
print(time.strftime("%H:%M:%S", time.gmtime(time.time()-start)) + ":16th model loaded. All model are loaded.")

@app.route('/api/judge', methods=['POST'])
def scoring():
    try:
        if request.method == "POST":
            content_type = request.headers["Content-Type"]
            if content_type == None or \
                    not content_type.startswith("application/json") or \
                    "img" not in request.json or \
                    "pattern" not in request.json:
                return make_response(jsonify({'status': 'failure'}), 405)

            base64str = request.json["img"]
            # POSTで渡ってきたbase64文字列は「+」が「 」(スペース)に変換されてしまっているため、元に戻してやってからDecode。
            dec_image = base64.b64decode(base64str.replace(" ", "+"))
            img = Image.open(io.BytesIO(dec_image))
            img = img.convert("RGB")
            img = img.resize((100, 100))
            data = np.asarray(img)
            data = data.astype("float") / 255

            img = data

            pred1 = s1model.predict(np.array([img]))
            pred2 = s2model.predict(np.array([img]))
            pred3 = s3model.predict(np.array([img]))
            pred4 = s4model.predict(np.array([img]))
            pred5 = ""
            pred6 = ""
            pred7 = ""
            pattern = request.json["pattern"]
            if pattern == "P1":
                pred5 = s5_P1model.predict(np.array([img]))
                pred6 = s6_P1model.predict(np.array([img]))
                pred7 = s7_P1model.predict(np.array([img]))
            elif pattern == "P2":
                pred5 = s5_P2model.predict(np.array([img]))
                pred6 = s6_P2model.predict(np.array([img]))
                pred7 = s7_P2model.predict(np.array([img]))
            elif pattern == "P3":
                pred5 = s5_P3model.predict(np.array([img]))
                pred6 = s6_P3model.predict(np.array([img]))
                pred7 = s7_P3model.predict(np.array([img]))
            elif pattern == "P4":
                pred5 = s5_P4model.predict(np.array([img]))
                pred6 = s6_P4model.predict(np.array([img]))
                pred7 = s7_P4model.predict(np.array([img]))

            category = {
                "1": {"category": np.argmax(pred1, axis=1).item(), "pred": pred1.tolist()[0]},
                "2": {"category": np.argmax(pred2, axis=1).item(), "pred": pred2.tolist()[0]},
                "3": {"category": np.argmax(pred3, axis=1).item(), "pred": pred3.tolist()[0]},
                "4": {"category": np.argmax(pred4, axis=1).item(), "pred": pred4.tolist()[0]},
                "5": {"category": np.argmax(pred5, axis=1).item(), "pred": pred5.tolist()[0]},
                "6": {"category": np.argmax(pred6, axis=1).item(), "pred": pred6.tolist()[0]},
                "7": {"category": np.argmax(pred7, axis=1).item(), "pred": pred7.tolist()[0]}
            }

            result = jsonify({
                "status": "OK",
                "category": category
            })

            return make_response(result, 200)
        else:
            return make_response(jsonify({'status': 'failure'}), 405)
    except:
        import traceback
        traceback.print_exc()
        return make_response(jsonify({"status": "failure", "message": "Internal server error"}), 500)


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

 開発環境:ローカル

  • Python 3.6.4 :: Anaconda
  • Windows 10
  • keras 2.1.4
  • tensorflow 1.2.1

※ちなみにローカルマシン(SurfacePro4)で上記コードを実行すると読み込み完了までおおよそ7分前後かかります

 実行環境:Azure Web Apps

  • 拡張機能によりPython 3.6.4をインストール
  • その他はAzurePortalから作成したまま
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

ウェブアプリの初期化に時間がかかりすぎてタイムアウトしているのであれば、以下のように別スレッドを立てて遅延ロードをしてあげれば良い気がします。

from collections import defaultdict
from threading import Thread

from flask import Flask


app = Flask(__name__)
models = defaultdict(lambda: 'Still loading')
keys = ('M1', 'M2', 'M3')


@app.route('/status', methods=['GET'])
def status():
    return '<br/>'.join(key + '=' + models[key] for key in keys)


def loader():
    from time import sleep
    for key in keys:
        sleep(30)
        models[key] = 'Ready'


Thread(target=loader, daemon=True).start()
if __name__ == '__main__':
    app.run()

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/13 20:00

    頂いたコードで概ね想定通りの実装ができそうです!ありがとうございました!!
    ところで1点気になったので教えていただきたく。。
    質問を投げた後に自分なりの別解で、モデルのloadを起動時ではなく別立てしたInit用APIに寄せ、モデル数分だけInitを呼んでから本当に叩きたいAPIを叩く形で、一応起動させることは叶っておりました。
    このとき、起動時に用意したモデル用の空変数「s1model」に別のAPIから値をセットするためには「global s1model」のおまじないが必要だったわけですが、頂いたコードでは直で「models」を書き換えているように見受けられます。
    若葉マーク付け忘れたんですがPython初心者のため、この辺りのカラクリがまだつかめていません。
    ご教示いただければ。。
    (いわゆる参照型と値型の違いとか?「s1model」は空文字初期化してるから値型で、「models」はdefaultdictで初期化してるから参照型の配列で、参照型配列の中身だから操作可能、、、みたいな?)

    キャンセル

  • 2018/03/13 20:07

    modelsはloaderの中でmodels[xxx]として参照してるのでglobalが不要なんです。これがもし参照ではなく代入をするのであればglobalをつけたほうが良いですね。

    キャンセル

  • 2018/03/13 20:10

    なるほど私の想像通り、参照変数の中身の操作だからOKってことですね。
    重ねてご回答ありがとうございました!

    キャンセル

  • 2018/03/13 22:09

    締めた後に申し訳ないのですが、頂いたエッセンスを組み込んだコードがどうにも意図した動きになりません。
    https://teratail.com/questions/117352
    こちら何かアドバイスがあればご教示いただきたく。。

    キャンセル

-2

単なる速度不足ではないでしょうか。

コンパイラ系の言語で組み直すほうがいいかと思われます

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.47%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Python 3.x

    10255questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Keras

    508questions

  • Flask

    365questions

    FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

  • Azure

    271questions

    Azureは、マイクロソフトのクラウド プラットフォームで、旧称は Windows Azureです。PaaSとIaaSを組み合わせることで、 コンピューティング・ストレージ・データ・ネットワーキング・アプリケーションなど多くの機能を持ちます。

  • IIS

    213questions

    IIS(Internet Information Services)はマイクロソフト社によって開発されたwebサーバーです。Windows上で動作します。