python模型部署到服务器_如何在后台部署深度学习模型?KerasRedis(内存数据结构存储)
Flask (Python的微web框架)
消息队列和消息代理编程范例
本篇⽂章的整体思路如下:
我们将⾸先简要讨论Redis数据存储,以及如何使⽤它促进消息队列和消息代理。然后,我们将通过安装所需的Python包来配置Python开发环境,以构建我们的Keras深度学习REST API。⼀旦配置了开发环境,就可以使⽤Flask web框架实现实际的Keras深度学习REST API。在实现之后,我们将启动Redis和Flask服务器,然后使⽤cURL和Python向我们的深度学习API端点提交推理请求。最后,我们将以对构建⾃⼰的深度学习REST API时应该牢记的注意事项的简短讨论结束。
第⼀部分:简要介绍Redis如何作为REST API消息代理/消息队列
spanker什么意思图⽚1:Redis可以⽤作我们深度学习REST API的消息代理/消息队列
Redis是内存中的数据存储。它不同于简单的键/值存储(⽐如memcached),因为它可以存储实际的数据结构。今天我们将使⽤Redis作为消息代理/消息队列。这包括:
在我们的机器上运⾏Redis
将数据(图像)按照队列的⽅式⽤Redis存储,并依次由我们的REST API处理
为新批输⼊图像循环访问Redis
对图像进⾏分类并将结果返回给客户端
⽂章中对Redis官⽹有⼀个超链接(redis.io/topics/introduction),但是要翻出去,所以我就截图⼀个图⽚放上去仅供参考。sql语句数字是哪个词
第⼆部分:安装和配置Redis
官⽹做法,linux系统的安装:
⾃⼰的安装⽅法:
conda install redis
开启⽅式相同:
resdis-server
结果:
测试和原⽂的命令⼀致。
第三部分:配置Python开发环境以构建Keras REST API
⽂章中说需要创建新的虚拟环境来防⽌影响系统级别的python项⽬(但是我没有创建),但是还是需要安装rest api所需要依赖的包。以下为所需要的包。
第四部分:实现可扩展的Keras REST API
⾸先是Keras Redis Flask REST API数据流程图
让我们开始构建我们的服务器脚本。为了⽅便起见,我在⼀个⽂件中实现了服务器,但是它可以按照您认为合适的⽅式模块化。为了获得最好的结果和避免复制/粘贴错误,我建议您使⽤本⽂的“下载”部分来获取相关的脚本和图像。
为了简单起见,我们将在ImageNet数据集上使⽤ResNet预训练。我将指出在哪⾥可以⽤你⾃⼰的模型交换ResNet。flask模块包含flask 库(⽤于构建web API)。redis模块将使我们能够与redis数据存储接⼝。从这⾥开始,让我们初始化将在run_keras_server.py中使⽤的常量.
我们将向服务器传递float32图像,尺⼨为224 x 224,包含3个通道。我们的服务器可以处理⼀个BATCH_SIZE = 32。如果您的⽣产系统上有GPU(s),那么您需要调优BATCH_SIZE以获得最佳性能。我发现将SERVER_SLEEP和CLIENT_SLEEP设置为0.25秒(服务器和客户端在再次轮询Redis之前分别暂停的时间)在⼤多数系统上都可以很好地⼯作。如果您正在构建⼀个⽣产系统,那么⼀定要调整这些常量。
让我们启动我们的Flask app和Redis服务器:
在这⾥你可以看到启动Flask是多么容易。在运⾏这个服务器脚本之前,我假设Redis服务器正在运⾏(之前的redis-server)。我们的Python脚本连接到本地主机6379端⼝(Redis的默认主机和端⼝值)上的Redis存储。不要忘记将全局Keras模型初始化为None。接下来我们来处理图像的序列化:
Redis将充当服务器上的临时数据存储。图像将通过诸如cURL、Python脚本甚⾄是移动应⽤程序等各种⽅法进⼊服务器,⽽且,图像只能每隔⼀段时间(⼏个⼩时或⼏天)或者以很⾼的速率(每秒⼏次)进⼊服务器。我们需要把图像放在某个地⽅,因为它们在被处理前排队。我们的Redis存储将作为临时存储。
为了将图像存储在Redis中,需要对它们进⾏序列化。由于图像只是数字数组,我们可以使⽤base64编码来序列化图像。使⽤base64编码还有⼀个额外的好处,即允许我们使⽤JSON存储图像的附加属
性。
base64_encode_image函数处理序列化。类似地,在通过模型传递图像之前,我们需要反序列化图像。这由base64_decode_image函数处理。
预处理图⽚
我已经定义了⼀个prepare_image函数,它使⽤Keras中的ResNet50实现对输⼊图像进⾏预处理,以便进⾏分类。在使⽤您⾃⼰的模型时,我建议修改此函数,以执⾏所需的预处理、缩放或规范化。
从那⾥我们将定义我们的分类⽅法
classify_process函数将在它⾃⼰的线程中启动,我们将在下⾯的__main__中看到这⼀点。该函数将从Redis服务器轮询图像批次,对图像进⾏分类,并将结果返回给客户端。
在model = ResNet50(weights="imagenet")这⼀⾏中,我将这个操作与终端打印消息连接起来——根据Keras模型的⼤⼩,加载是即时的,或者需要⼏秒钟。
加载模型只在启动这个线程时发⽣⼀次——如果每次我们想要处理⼀个映像时都必须加载模型,那么速度会⾮常慢,⽽且由于内存耗尽可能导致服务器崩溃。
加载模型后,这个线程将不断轮询新的图像,然后将它们分类(注意这部分代码应该时尚⼀部分的继续)
在这⾥,我们⾸先使⽤Redis数据库的lrange函数从队列(第79⾏)中获取最多的BATCH_SIZE图像。
从那⾥我们初始化imageIDs和批处理(第80和81⾏),并开始在第84⾏开始循环队列。
在循环中,我们⾸先解码对象并将其反序列化为⼀个NumPy数组image(第86-88⾏)。
接下来,在第90-96⾏中,我们将向批处理添加图像(或者如果批处理当前为None,我们将该批处理设置为当前图像)。
我们还将图像的id附加到imageIDs(第99⾏)。
让我们完成循环和函数
在这个代码块中,我们检查批处理中是否有图像(第102⾏)。如果我们有⼀批图像,我们通过模型(第105⾏)对整个批进⾏预测。从那⾥,我们循环⼀个图像和相应的预测结果(110-122⾏)。这些⾏向输出列表追加标签和概率,然后使⽤imageID将输出存储在Redis数据库中(第116-122⾏)。
我们使⽤第125⾏上的ltrim从队列中删除了刚刚分类的图像集。最后,我们将睡眠设置为SERVER_SLEEP时间并等待下⼀批图像进⾏分类。下⾯我们来处理/predict我们的REST API端点
稍后您将看到,当我们发布到REST API时,我们将使⽤/predict端点。当然,我们的服务器可能有多个端点。我们使⽤@app。路由修饰符以第130⾏所⽰的格式在函数上⽅定义端点,以便Flask知道调⽤什么函数。我们可以很容易地得到另⼀个使⽤AlexNet⽽不是ResNet的端点,我们可以⽤类似的⽅式定义具有关联函数的端点。你懂的,但就我们今天的⽬的⽽⾔,我们只有⼀个端点叫做/predict。
我们在第131⾏定义的predict⽅法将处理对服务器的POST请求。这个函数的⽬标是构建JSON数据,并将其发送回客户机。如果POST数据包含图像(第137和138⾏),我们将图像转换为PIL/Pillow格式,并对其进⾏预处理(第141-143⾏)。
在开发这个脚本时,我花了⼤量时间调试我的序列化和反序列化函数,结果发现我需要第147⾏将数组转换为C-contiguous排序(您可以在这⾥了解更多)。⽼实说,这是⼀个相当⼤的⿇烦事,但我希望它能帮助你站起来,快速跑。
如果您想知道在第99⾏中提到的id,那么实际上是使⽤uuid(通⽤唯⼀标识符)在第151⾏⽣成的。我们使⽤UUID来防⽌hash/key冲突。
python请求并解析json数据接下来,我们将图像的id和base64编码附加到d字典中。使⽤rpush(第153⾏)将这个JSON数据推送到Redis db⾮常简单。
让我们轮询服务器以返回预测
我们将持续循环,直到模型服务器返回输出预测。我们开始⼀个⽆限循环,试图得到157-159条预测线。从这⾥,如果输出包含预测,我们将对结果进⾏反序列化,并将结果添加到将返回给客户机的数据中。我们还从db中删除了结果(因为我们已经从数据库中提取了结果,不再需要将它们存储在数据库中),并跳出了循环(第163-172⾏)。
否则,我们没有任何预测,我们需要睡觉,继续投票(第176⾏)。如果我们到达第179⾏,我们已经成功地得到了我们的预测。在本例中,我们向客户机数据添加True的成功值(第179⾏)。注意:对于这个⽰例脚本,我没有在上⾯的循环中添加超时逻辑,这在理想情况下会为数据添加⼀个False的成功值。我将由您来处理和实现。最后我们称烧瓶。jsonify对数据,并将其返回给客户端(第182⾏)。这就完成了我们的预测函数。
为了演⽰我们的Keras REST API,我们需要⼀个__main__函数来实际启动服务器
第186-196⾏定义了__main__函数,它将启动classify_process线程(第190-192⾏)并运⾏Flask应⽤程序(第196⾏)。
第五部分:启动可伸缩的Keras REST API
要测试我们的Keras深度学习REST API,请确保使⽤本⽂的“下载”部分下载源代码⽰例图像。从这⾥,让我们启动Redis服务器,如果它还没有运⾏:
redis-server
然后,在另⼀个终端中,让我们启动REST API Flask服务器:
python run_keras_server.py
另外,我建议在向服务器提交请求之前,等待您的模型完全加载到内存中。现在我们可以继续使⽤cURL和Python测试服务器。
第七部分:使⽤cURL访问Keras REST API
使⽤cURL来测试我们的Keras REST API服务器。这是我的家庭⼩猎⽝Jemma。根据我们的ResNet模型,她被归类为⼀只拥有94.6%⾃信的⼩猎⽝。
curl -X POST -F image=@jemma.png 'localhost:5000/predict'
你会在你的终端收到JSON格式的预测:
{"predictions": [{"label": "beagle","probability": 0.9461546540260315},{"label": "bluetick","probability":
0.031958919018507004},{"label": "redbone","probability": 0.006617196369916201},{"label":
"Walker_hound","probability": 0.0033879687543958426},{"label": "Greater_Swiss_Mountain_dog","probability":
0.0025766862090677023}],"success": true}
access下载以及破解第六部分:使⽤Python向Keras REST API提交请求
如您所见,使⽤cURL验证⾮常简单。现在,让我们构建⼀个Python脚本,该脚本将发布图像并以编程⽅式解析返回的JSON。
让我们回顾⼀下simple_request.py
# import the necessary packagesimport requests# initialize the Keras REST API endpoint URL along with the input# image pathKERAS_REST_API_URL = "localhost:5000/predict"IMAGE_PATH = "jemma.png"
我们在这个脚本中使⽤Python请求来处理向服务器提交数据。我们的服务器运⾏在本地主机上,可以通过端⼝5000访问端点/predict,这是KERAS_REST_API_URL变量(第6⾏)指定的。
我们还定义了IMAGE_PATH(第7⾏)。png与我们的脚本在同⼀个⽬录中。如果您想测试其他图像,请确保指定到您的输⼊图像的完整路径。
让我们加载图像并发送到服务器:
精子分级直方图怎么看# load the input image and construct the payload for the requestimage = open(IMAGE_PATH, "rb").read()payload = {"image": image}# submit the requestr = requests.post(KERAS_REST_API_URL, files=payload).json()# ensure the request was sucessfulif r["success"]:# loop over the predictions and display themfor (i, result) in enumerate(r["predictions"]):print("{}. {}: {:.4f}".format(i + 1, result["label"],result["probability"]))# otherwise, the request failedelse:print("Request failed")
我们在第10⾏以⼆进制模式读取图像并将其放⼊有效负载字典。负载通过请求发送到服务器。在第14⾏发布。如果我们得到⼀个成功消息,我们可以循环预测并将它们打印到终端。我使这个脚本很简单,但是如果你想变得更有趣,你也可以使⽤OpenCV在图像上绘制最⾼的预测⽂本。
第七部分:运⾏简单的请求脚本
编写脚本很容易。打开终端并执⾏以下命令(当然,前提是我们的Flask服务器和Redis服务器都在运⾏)。
python simple_request.py
使⽤Python以编程⽅式使⽤我们的Keras深度学习REST API的结果
python怎么读啊第⼋部分:扩展深度学习REST API时的注意事项
如果您预期在深度学习REST API上有较长⼀段时间的⾼负载,那么您可能需要考虑⼀种负载平衡算法,例如循环调度,以帮助在多个GPU 机器和Redis服务器之间平均分配请求。
记住,Redis是内存中的数据存储,所以我们只能在队列中存储可⽤内存中的尽可能多的图像。
使⽤float32数据类型的单个224 x 224 x 3图像将消耗602112字节的内存。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论