flask+keras+tensorflowのwebアプリをherokuにデプロイした際に、「R14 - Memory Quota Exceeded」エラーしたため、その解決方法を教えていただきたく、質問しました。
前提・実現したいこと
flask+keras+tensorflowで学習済みモデルをload_moduleし、アップロードした画像の判別をするwebアプリを実装していました。
webアプリ:https://tongue-classify.herokuapp.com
ローカル環境ならびにデプロイしたアプリはPC上では正常に作動しました。が、スマホで作動させた場合に下記のようなR14エラーが発生してしまいます。
R14エラーがdynoメモリ不足によって起こると知ったため、何らかの方法でメモリ使用量を削減し、herokuの無料プラン内で作動させたいと思いました。
発生している問題・エラーメッセージ
$ heroku logs --tail 2019-10-10T10:07:32.591876+00:00 heroku[web.1]: Process running mem=534M(104.3%) 2019-10-10T10:07:32.593706+00:00 heroku[web.1]: Error R14 (Memory quota exceeded) 2019-10-10T10:08:36.672094+00:00 heroku[web.1]: source=web.1 dyno=heroku.148920410.8ea5bc70-23aa-4b88-99c4-f7fd91b197a9 sample#load_avg_1m=0.34 2019-10-10T10:08:36.688775+00:00 heroku[web.1]: source=web.1 dyno=heroku.148920410.8ea5bc70-23aa-4b88-99c4-f7fd91b197a9 sample#memory_total=509.29MB sample#memory_rss=479.70MB sample#memory_cache=0.05MB sample#memory_swap=29.54MB sample#memory_pgpgin=323173pages sample#memory_pgpgout=204444pages sample#memory_quota=512.00MB 2019-10-10T10:08:42.477268+00:00 heroku[router]: at=info method=POST path="/predict" host=tongue-classify.herokuapp.com request_id=b05077d4-d660-49af-82db-9e84bffe02dd fwd="125.200.127.226" dyno=web.1 connect=0ms service=2820ms status=500 bytes=455 protocol=https
該当のソースコード
python
1import os 2from flask import Flask, flash, request, redirect, url_for, render_template 3from werkzeug.utils import secure_filename 4 5from keras.models import Sequential, load_model 6import keras, sys 7import numpy as np 8from PIL import Image 9import keras.backend as K 10import tensorflow as tf 11 12 13config = tf.ConfigProto() 14config.gpu_options.allow_growth = True 15sess = tf.Session(config=config) 16K.set_session(sess) 17 18 19class_1 = "1です。" 20class_content_1 = "舌苔はほとんどついておらず、かなり綺麗な状態かと思われます。" 21classes_solution_1 = "これまでと同様に、口腔ケアを頑張っていきましょう。注意点として、舌磨きのやりすぎは舌を傷つけるだけでなく舌苔の付着量増加にもつながるため、1日1回を限度に優しく行うようにしましょう。" 22 23class_2 = "2です。" 24class_content_2 = "舌の汚れはあまり気にする必要はないでしょう。" 25classes_solution_2 = "舌苔は口臭の原因にもなるため、ああああああああああああああああああああああああああああああああああああああああああああああああああああ。" 26 27class_3 = "3です。" 28class_content_3 = "舌に汚れが若干多くついているかもしれません。舌苔(舌の汚れ)は口臭の原因にもなるため、普段の歯磨きに加え、舌を掃除することが望ましいでしょう。" 29classes_solution_3 = "舌苔の掃除法としてはガーゼや舌ブラシを用いて、舌の表面を奥から手前に優しくこするようにして行ってください。1日1回を限度に行いましょう。" 30 31classes = [class_1,class_2,class_3] 32classes_content = [class_content_1, class_content_2, class_content_3] 33classes_solution = [classes_solution_1, classes_solution_2, classes_solution_3] 34 35num_classes = len(classes) 36image_size = 50 37 38UPLOAD_FOLDER = './uploads' 39ALLOWED_EXTENSIOS = set(['png', 'jpg', 'gif']) 40 41app = Flask(__name__) 42app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER 43 44 45 46model = load_model('./tongue_cnn_aug.h5') 47graph = tf.get_default_graph() 48 49 50 51def allowed_file(filename): 52 #もし.が含まれたる かつ もし拡張子前の.で区切り、拡張子小文字小文字であれば true 53 return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIOS 54 55# @app.route('/', methods=['GET', 'POST']) 56@app.route('/') 57def upload_file(): 58 return render_template('test.html') 59 60@app.route('/predict',methods=['GET', 'POST']) 61def predict_file(): 62 global graph 63 with graph.as_default(): 64 if request.method == 'POST': 65 if 'file' not in request.files: 66 flash('ファイルがありません') 67 return redirect(request.url) 68 file = request.files['file'] #ここがよくワカンねぇな 69 # if user does not selectfile, browser also 70 #submit an empty part without filename 71 if file.filename == '': 72 flash('ファイルがありません') 73 return redirect(request.url) 74 if file and allowed_file(file.filename): 75 filename = secure_filename(file.filename) 76 file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 77 filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) 78 79 image = Image.open(filepath) 80 image = image.convert('RGB') 81 image = image.resize((image_size, image_size)) 82 data = np.asarray(image) 83 X = [] 84 X.append(data) 85 X = np.array(X) 86 87 #model = load_model('./tongue_cnn_aug.h5') 88 89 result = model.predict([X])[0] 90 predicted = result.argmax() 91 percentage = int(result[predicted] * 100) 92 #K.clear_session() 93 result_title = "" 94 result_content="" 95 result_solution="" 96 97 98 # return render_template('predict.html', result = classes[predicted], result_content = classes_content[predicted], result_solution = classes_solution[predicted], result_title="解析結果") 99 return render_template('predict.html', result = classes[predicted], result_content = classes_content[predicted], result_solution = classes_solution[predicted], result_title="解析結果",img_name=file.filename) 100 101from flask import send_from_directory 102 103@app.route('/uploads/<filename>') 104def uploaded_file(filename): 105 return send_from_directory(app.config['UPLOAD_FOLDER'], filename) 106 107
試したこと
を参考にkerasのメモリを制限しようと試みたものの、エラーは消えませんでした。
###追記
メモリの使用量
Line # Mem usage Increment Line Contents ================================================ 51 332.6 MiB 332.6 MiB @app.route('/') 52 @profile 53 def upload_file(): 54 332.8 MiB 0.2 MiB return render_template('test.html') 127.0.0.1 - - [12/Oct/2019 17:15:04] "GET / HTTP/1.1" 200 - I1012 17:15:04.182286 123145472167936 _internal.py:122] 127.0.0.1 - - [12/Oct/2019 17:15:04] "GET / HTTP/1.1" 200 - Filename: /Users/yuya/PycharmProjects/tongue/predict_web.py Line # Mem usage Increment Line Contents ================================================ 58 332.8 MiB 332.8 MiB @app.route('/predict',methods=['GET', 'POST']) 59 @profile 60 def predict_file(): 61 global graph 62 332.8 MiB 0.0 MiB with graph.as_default(): 63 332.8 MiB 0.0 MiB if request.method == 'POST': 64 332.8 MiB 0.0 MiB if 'file' not in request.files: 65 flash('ファイルがありません') 66 return redirect(request.url) 67 332.8 MiB 0.0 MiB file = request.files['file'] #ここがよくワカンねぇな 68 # if user does not selectfile, browser also 69 #submit an empty part without filename 70 332.8 MiB 0.0 MiB if file.filename == '': 71 flash('ファイルがありません') 72 return redirect(request.url) 73 332.8 MiB 0.0 MiB if file and allowed_file(file.filename): 74 332.8 MiB 0.0 MiB filename = secure_filename(file.filename) 75 332.8 MiB 0.0 MiB file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 76 332.8 MiB 0.0 MiB filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) 77 78 333.2 MiB 0.4 MiB image = Image.open(filepath) 79 337.9 MiB 4.7 MiB image = image.convert('RGB') 80 337.9 MiB 0.0 MiB image = image.resize((image_size, image_size)) 81 337.9 MiB 0.0 MiB data = np.asarray(image) 82 337.9 MiB 0.0 MiB X = [] 83 337.9 MiB 0.0 MiB X.append(data) 84 337.9 MiB 0.0 MiB X = np.array(X) 85 86 #model = load_model('./tongue_cnn_aug.h5') 87 88 341.7 MiB 3.8 MiB result = model.predict([X])[0] 89 341.7 MiB 0.0 MiB predicted = result.argmax() 90 341.7 MiB 0.0 MiB percentage = int(result[predicted] * 100) 91 #K.clear_session() 92 341.7 MiB 0.0 MiB result_title = "" 93 341.7 MiB 0.0 MiB result_content="" 94 341.7 MiB 0.0 MiB result_solution="" 95 96 97 # return render_template('predict.html', result = classes[predicted], result_content = classes_content[predicted], result_solution = classes_solution[predicted], result_title="解析結果") 98 341.8 MiB 0.1 MiB return render_template('predict.html', result = classes[predicted], result_content = classes_content[predicted], result_solution = classes_solution[predicted], result_title="解析結果",img_name=file.filename)
学習済みモデルがどれぐらいメモリを使用しているのかの確認方法が調べても分かりませんでしたので、memory_profilerを使って各行のメモリ使用量を確認してみました。
load_moduleした学習モデル「tongue_cnn_aug.h5」のメモリ使用料の確認方法も教えていただけたらと思います。
訂正依頼でgpuについての言及がありましたが、gpuと当プログラムの関係はなかったものの、もしかしたらメモリが減るのでは?と思い試してみました。
回答1件
あなたの回答
tips
プレビュー