热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PyTorch模型的服务化部署

本文将介绍如何使用Flask搭建一个基于PyTorch的图片分类服务以及并行处理的相关技术。作为一个深度学习工程师,学习这些内容纯粹是为了方便对服务化的模型进行deb

本文将介绍如何使用Flask搭建一个基于PyTorch的图片分类服务以及并行处理的相关技术。作为一个深度学习工程师,学习这些内容纯粹是为了方便对服务化的模型进行debug,因为web开发的同时常常表示他们很难定位到深度学习服务的bug的位置。

1. 环境

系统:Ubuntu 18.04

Python版本:3.7

依赖Python包:

  1. PyTorch==1.3
  2. Flask==0.12
  3. Gunicorn

需要注意的是Flask 0.12中默认的单进程单线程,而最新的1.0.2则不是(具体是多线程还是多进程尚待考证),而中文博客里面能查到的资料基本都在说Flask默认单进程单线程。

依赖工具

  1. nginx
  2. apache2-utils

nginx 用于代理转发和负载均衡,apache2-utils用于测试接口


2. 搭建异步服务

对于做算法的读者,不着急搭建深度学习模型,因为算法工程师普遍对web开发不太熟悉,可以先搭建一个最简单的web服务,并验证其功能无误之后再加入深度学习模型。

2.1 Flask搭建图片上传服务

因为图片分类服务需要从本地上传图片,可以先搭建一个用于图片上传的服务

# sim_server.py
from flask import Flask, request
from werkzeug.utils import secure_filename
import uuid
from PIL import Image
import os
import timeapp = Flask(__name__)@app.route("/run",methods = ["GET"])
def run():# 用于测试服务是否并行time.sleep(1)return "0"if __name__ == "__main__":app.run(host="0.0.0.0",port=5555,debug=True)

启动服务:

python sim_server.py

此时可以使用apache-utils测试接口是否是异步运行

ab -c 2 -n 10 http://localhost:5555/run

得到一长串结果,其中有一行是:

Requests per second: 1.00 [#/sec] (mean)

这行显示的是服务每秒钟能处理几个请求,如果是单进程单线程的话,每秒钟只能处理一个请求,服务的处理能力会随着进程数的增加而增加,但是由于计算机性能限制,增加进程数带来的处理能力提升会越来越小。

2.2 使用gunicorn启动多个进程

使用gunicorn可以快速启动多个进程:

gunicorn -w 4 -b 0.0.0.0:5555 sim_server:app

输出如下内容代表服务创建成功:

[2020-02-11 14:50:24 +0800] [892] [INFO] Starting gunicorn 20.0.4
[2020-02-11 14:50:24 +0800] [892] [INFO] Listening at: http://0.0.0.0:5555 (892)
[2020-02-11 14:50:24 +0800] [892] [INFO] Using worker: sync
[2020-02-11 14:50:24 +0800] [895] [INFO] Booting worker with pid: 895
[2020-02-11 14:50:24 +0800] [896] [INFO] Booting worker with pid: 896
[2020-02-11 14:50:24 +0800] [898] [INFO] Booting worker with pid: 898
[2020-02-11 14:50:24 +0800] [899] [INFO] Booting worker with pid: 899

再次使用apache-utils进行测试,可以看到处理能力的提升:

ab -c 4 -n 10 http://localhost:5555/run

得到处理能力:Requests per second: 3.33 [#/sec] (mean)

可以看到,开启四个进程之后,服务的处理能力并没有达到4requests/second。

如果配置比较复杂,也可以将配置写入一个文件中,如:

bind = '0.0.0.0:5555'
timeout = 10
workers = 4

然后运行:

gunicorn -c gunicorn.conf sim_server:app

2.3 nginx代理

安装好nginx之后,修改nginx的配置文件

worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;events {worker_connections 1024;
}http {server{listen 5556; # nginx端口server_name localhost;location / {proxy_pass http://localhost:5555/run; # gunicorn的url}}
}

然后按配置文件启动

sudo nginx -c nginx.conf

启动之后就可以在新的地址上访问了:

ab -c 4 -n 10 http://localhost:5556/run

3. 将PyTorch分类模型接入服务

from flask import Flask, request
from werkzeug.utils import secure_filename
import uuid
from PIL import Image
import os
import time
import base64
import jsonimport torch
from torchvision.models import resnet18
from torchvision.transforms import ToTensorfrom keys import keyapp = Flask(__name__)
net = resnet18(pretrained=True)
net.eval()@app.route("/",methods=["GET"])
def show():return "classifier api"@app.route("/run",methods = ["GET","POST"])
def run():file = request.files['file']base_path = os.path.dirname(__file__)if not os.path.exists(os.path.join(base_path, "temp")):os.makedirs(os.path.join(base_path, "temp"))file_name = uuid.uuid4().hexupload_path = os.path.join(base_path, "temp", file_name)file.save(upload_path)img = Image.open(upload_path)img_tensor = ToTensor()(img).unsqueeze(0)out = net(img_tensor)pred = torch.argmax(out,dim = 1)return "result : {}".format(key[pred])if __name__ == "__main__":app.run(host="0.0.0.0",port=5555,debug=True)

并发测试

使用apache2-utils进行上传图片的post请求方法参考:
https://links.jianshu.com/go?to=https%3A%2F%2Fgist.github.com%2Fchiller%2Fdec373004894e9c9bb38ac647c7ccfa8

严格参照,注意一个标点,一个符号都不要错。
使用这种方法传输图片的base64编码,在服务端不需要解码也能使用

然后使用下面的方式访问
gunicorn 接口

ab -n 2 -c 2 -T "multipart/form-data; boundary=1234567890" -p turtle.txt http://localhost:5555/run

nginx 接口

ab -n 2 -c 2 -T "multipart/form-data; boundary=1234567890" -p turtle.txt http://localhost:5556/run

有了gunicorn和nginx就可以轻松地实现PyTorch模型的多机多卡部署了。

reference

https://www.jianshu.com/p/a4c4a89a771d


推荐阅读
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 小程序自动授权和手动接入的方式及操作步骤
    本文介绍了小程序支持的两种接入方式:自动授权和手动接入,并详细说明了它们的操作步骤。同时还介绍了如何在两种方式之间切换,以及手动接入后如何下载代码包和提交审核。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
author-avatar
傻丫丫69_678
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有