www.v2ex.com/t/588188#reply0 那天发了上面这个,各位见笑了。主要是我那天想,就一个全局变量这么简单的问题,居然还报错,花了我一整天。 不过最终还是解决了,那下面给出解决方案供大家参考吧。
场景是,我想用 flask+imageai 来分析处理一张图片。
一开始把 imageai 的加载 model 和处理都放在一个函数里了,然后别人访问这个 url,就调用分析处理函数,处理这图片(这一步是没有出现问题的)。
然后发现了一个缺点是每次都要 loadModel 一次,效率大打折扣。既然每次都用的同一个训练模型(目前 imageai 的图像识别只提供了两种模型),没必要每次访问 url 都 load 一次模型吧。
之后理所当然得将初始化( loadModel 之类)的步骤都放全局里,想着就是刚启动服务器就直接加载模型,之后访问 url 就直接执行分析操作就好。
结果发现迷之报错,加上我本身也不做机器学习这块,还不懂报错的原因是什么。。。去 imageai 的 issue 里看 OlafenwaMoses/ImageAI/issues/159 说是设置成 global 可以防止多次加载 model 就行了,但是也没解决。
没法,只好从根源错误找起。 根源好像是 tensorflow 模块(毕竟 imageai 也依赖了这个库)报的错,ValueError: Tensor Tensor("keras_learning_phase:0", shape=(), dtype=bool) is not an element of this graph. 然后又去 Stack Overflow 里搜了一堆 TensorFlow 的问题,说起来也没怎么懂,不过大概看出来以上错误有不少情况都是因为在 tensorflow 中用了多线程 /多进程导致的。 既然如此,突然想起,那就把 flask 的多线程、多进程禁用了吧。 结果,居然成了。反正就公司内部里用用,不用多线程就不用吧,也没啥。
下面的是我的成功了的代码。
from flask import Flask, Response, jsonify
app = Flask(__name__)
import os
from imageai.Detection import ObjectDetection
import time
import json
execution_path = os.getcwd()
st = time.time()
detector = ObjectDetection()
detector.setModelTypeAsRetinaNet()
detector.setModelPath(os.path.join(execution_path, "model", "resnet50_coco_best_v2.0.1.h5"))
# detector.setModelTypeAsTinyYOLOv3()
# detector.setModelPath(os.path.join(execution_path, "model", "yolo-tiny.h5"))
detector.loadModel()
# detector.loadModel(detection_speed="fastest")
print(f'Init Timer: {time.time()-st}')
@app.route('/detect/<pic_name>')
def boat_detection(pic_name):
st = time.time()
results = getDetections(pic_name)
print(f'Sum Timer: {time.time()-st}')
msg = {}
for i, result in enumerate(results, 1):
result['percentage_probability'] = float(result['percentage_probability'])
result['box_points'] = list(result['box_points'])
for index in range(len(result['box_points'])):
result['box_points'][index] = int(result['box_points'][index])
result['box_points'] = tuple(result['box_points'])
msg[str(i)] = json.dumps(result)
return jsonify(msg)
def getDetections(file_name):
start = time.time()
image_folder = os.path.join(execution_path, 'data\\ship2\\')
output_folder = os.path.join(execution_path, 'data\\output\\')
st1 = time.time()
image_file = os.path.join(image_folder, file_name)
new_image_file = os.path.join(output_folder, file_name)
print(image_file, "-->", new_image_file)
if not os.path.exists(image_file):
print("not exist.")
return
# global detector
custom_objects = detector.CustomObjects(boat=True)
detections = detector.detectCustomObjectsFromImage(
custom_objects=custom_objects,
input_image=image_file,
output_image_path=new_image_file,
minimum_percentage_probability=30)
print(f'[Info]识别到 boat{len(detections)}艘')
for eachObject in detections:
print(eachObject.items())
end = time.time()
print(f'Excute Timer: {end-st1}')
print ("耗时: ",end-start)
return detections
if __name__ == '__main__':
app.run(threaded=False)
重点就是在 app.run()里设置成禁用多线程、多进程,global 有没有都无所谓。毕竟没涉及到修改 detector。
1
xiaolinjia OP 最近又学习了一些机器识别的问题,发现 keras 有个 session.graph 存放了这个 model,而 imageai 有一个参数可以设置用 session.graph。也就是多线程共用一个 model。
因此,如果 flask 开启多线程也没事。只需要在该方法里添加个参数 thread_safe=True 即可,如下: detections = detector.detectCustomObjectsFromImage( custom_objects=custom_objects, input_image=image_file, output_image_path=new_image_file, minimum_percentage_probability=30, thread_safe=True, ) |