目标检测Flask+Docker在服务器部署YOLOv5应用

Posted zstar_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了目标检测Flask+Docker在服务器部署YOLOv5应用相关的知识,希望对你有一定的参考价值。


前言

看到不少文章讲解用Flask部署YOLOv5的,不过基本都在本地上能够运行而戛然而止。因此,我打算再进一步,利用Docker在云服务器上部署YOLOv5,这样就能够开放给别人使用。

代码仓库:​​https://github.com/zstar1003/yolov5-flask​

本地部署

本地项目主要参考了robmarkcole的这个项目[1],原始项目是一年前多发布的大概用的是YOLOv5较早的版本,直接download下来会出现一些问题。于是我使用YOLOv5-5.0版本进行了重构。

项目结构

整体项目结构如下图所示:

【目标检测】Flask+Docker在服务器部署YOLOv5应用_python

  • models:存放模型构建相关程序,直接从yolov5-5.0版本中clone过来
  • utils:存放绘图、数据加载等相关工具,直接从yolov5-5.0版本中clone过来
  • static:存放前端渲染等静态文件
  • templates:前端页面html文件
  • webapp.py:入口程序

整体看来,整个项目作为一个demo还是比较简单的。当然utils和models里面存在一定的冗余,有的工具类是为训练测试提供服务,这里仅需要做推理即可。本来想着再进一步精简,不过发现各函数之间相关性挺大,遂不去修改。

快速运行

仓库里已经存放了yolov5s.pt文件,无需额外下载模型文件。
在终端运行​​​python webapp.py​​​,稍等片刻,即可访问​​ http://127.0.0.1:5000​

【目标检测】Flask+Docker在服务器部署YOLOv5应用_flask_02


在首页中选择文件再上传,即可返回出模型预测结果。预测后的图片会保存在​​static​​文件夹下。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_python_03

代码简析

核心代码:

@app.route("/", methods=["GET", "POST"])
def predict():
if request.method == "POST":
if "file" not in request.files:
return redirect(request.url)
file = request.files["file"]
if not file:
return

img_bytes = file.read()
img = Image.open(io.BytesIO(img_bytes))
img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

if img is not None:
showimg = img
with torch.no_grad():
img = letterbox(img, new_shape=imgsz)[0]
# Convert
# BGR to RGB, to 3x416x416
img = img[:, :, ::-1].transpose(2, 0, 1)
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(device)
img = img.half() if model.half() else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
pred = model(img)[0]

# Apply NMS
pred = non_max_suppression(pred, conf_thres, iou_thres)
# Process detections
for i, det in enumerate(pred): # detections per image
if det is not None and len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(
img.shape[2:], det[:, :4], showimg.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
label = %s %.2f % (names[int(cls)], conf)
plot_one_box(
xyxy, showimg, label=label, color=colors[int(cls)], line_thickness=2)

imgFile = "static/img.jpg"
cv2.imwrite(imgFile, showimg)

return redirect(imgFile)

return render_template("index.html")

前端通过POST的方式,将图片提交到后端,首先需要判断图片是否为空,如果为空,则返回空值,即报错界面;如果不是空值,则通过​​file.read()​​​来读取图片字节串,原代码是通过​​PIL.Image​​​来转成图片,为了和后面的推理过程兼容,转换成OpenCV格式。
推理部分代码基本完全copy自YOLOv5的​​​detect.py​​,推理之后的图片首先进行保存,然后再返回给前端进行直接显示。

云端部署

在服务器部署也有多种方案,最容易想到的就是直接在服务器搭建python环境,不过考虑到还需要安装torch这种大型库,出错概率高,因此更方便的就是使用Docker进行部署。

简单理解,Docker就像是一个自带了虚拟环境和程序的容器,只需要将其打包放在服务器,直接就可以运行。

生成requirements.txt

第一步是需要生成依赖文件列表​​requirements.txt​​​,以便在Docker Image中能够配置好需要的依赖。
通常的做法是这样进行生成:

pip freeze > requirements.txt

然后就可以在新环境中,这样快速安装:

pip install -r requirements.txt

但是这样做的一个巨大问题是,它会将环境中所有的库名称和版本进行输出,有些库是在项目中没有用到的,但依然会进行输出。

为了避免这种情况,有人就开发了一个​​pipreqs​​库,它可以进行一些过滤,仅将工程中用到的库和版本进行输出。

pipreqs的安装可以有两种方式:
方式一:

pip install pipreqs

方式二:
如果pip失败,可以去Github上克隆该项目,然后运行​​​setup.py​

git clone https://github.com/bndr/pipreqs.git
python setup.py install

安装好之后,再在当前目录下运行

pipreqs . --encoding=utf8 --force

这样就能生成​​requirement.txt​

coremltools==5.2.0
Flask==2.2.2
matplotlib==3.5.2
numpy==1.21.5
onnx==1.12.0
pafy==0.5.5
pandas==1.3.5
Pillow==9.2.0
PyYAML==6.0
requests==2.28.1
scipy==1.7.3
seaborn==0.11.2
setuptools==63.2.0
torch==1.11.0
torchvision==0.12.0
tqdm==4.64.0
opencv-python==4.6.0.66

注意生成之后需要检查一下,比如云服务器不具有GPU环境,那么就手动将torch改成CPU版本。

构建DockerFile

DockerFile是构建文件,包含了所有环境配置步骤,比如安装库。

常用指令有这些[2]:

FROM             # 基础镜像,一切从这里开始构建  centos
MAINTAINER # 镜像是谁写的, 姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤,tomcat镜像,这个tomcat压缩包!添加内容 添加同目录
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露端口配置 和我们的-p一样的
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行ONBUILD的指令,触发指令。
COPY # 类似ADD,将我们文件拷贝到镜像中
ENV # 构建的时候设置环境变量

构建的DockerFile内容如下:

FROM python:3.7-slim-buster

RUN apt-get update
RUN apt-get install ffmpeg libsm6 libxext6 -y

WORKDIR /app
ADD . /app
RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

EXPOSE 5000

CMD ["python", "webapp.py"]

首先,从​​python:3.7-slim-buster​​这个公开镜像中,引入python环境,然后更新数据源,安装三个必要的工具。

之后,指定工作路径在​​/app​​这个文件夹下,这个路径设置很重要,后面会用到。

然后就安装​​requirements.txt​​里面所列的所有依赖,注意这里使用了阿里源,这样可以进行加速。

接着暴露出5000这个端口,因为后面要通过这个端口号进行访问。

最后​​CMD​​​指定容器运行之后就执行的命令,即容器一旦运行就执行​​python webapp.py​​,将程序跑起来。

Docker打包上传

在注册之前,需要在本地安装Docker并进行注册,Windows系统可以安装Docekr的客户端,这里不做赘述。

打开客户端之后,就能在本地启动Docker服务,然后进入到项目终端,输入

docker build --tag zstar1003/yolov5-flask .

注意最后面有个点,这代表着将所有内容打包成镜像。​​--tag​​​指定了镜像名称,注意前面必须是用户名,否则后面将不能够进行拉取。如果前面tag忘记添加用户名,可以在打包之后进行更名,使用​​docker tag 原始名 zstar1003/yolov5-flask​

关于Docker的命名规则,可以看​​这篇文章​​[3],讲解得较为详细。

打包过程比较漫长,因为系统需要去联网下载前面那些依赖,打包完成之后,在终端输入​​docker images​​可以看到本地的所有镜像。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_docker_04


本地有了镜像之后,再将其​​push​​到公开仓库,这样方便后续拉取,执行命令:

docker push zstar1003/yolov5-flask

上传的过程也比较长,主要取决于镜像的大小和网速,上传完之后,可以在客户端的这个位置看到。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_docker_05

Docker镜像拉取

下面就是在云服务器上进行操作了,推荐使用​​FinalShell​​连接云服务器。

首先需要在云服务器上安装Docker,我是用的云服务器系统是Centos 7.6。
先装一些必要的软件包

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

设置稳定的仓库

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

安装社区版的Docker

sudo yum install docker-ce docker-ce-cli containerd.io

安装好之后,启动Docker

systemctl start docker

然后就可以拉取之前上传的镜像

docker pull zstar1003/yolov5-flask

拉取好之后,输入​​docker image -a​​看看镜像是否存在,存在就说明拉取成功。

Docker创建容器并启动

拉取完镜像之后,就需要创建一个容器来挂载镜像,主要命令是​​docker run​​,有下列这些可选参数,更多命令可参考[4]

docker run [Options] image

#参数说明
--name="名字" 指定容器名字
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口
-p ip:主机端口:容器端口 配置主机端口映射到容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
-P 随机指定端口
-e 环境设置
-v 容器数据卷挂载

于是输入​​docker run -p 5000:5000 zstar1003/yolov5-flask​​来创建一个容器来运行这个镜像,结果报错,原因是5000端口被占用。这是因为我这台服务器上之前跑过别的项目,5000端口有其它的进程在工作。

遇到这情况,可以用​​lsof​​来查询冲突的端口存在哪个进程。

首先安装​​lsof​

yum -y install lsof

然后输入

lsof -i :5000

如下图所示,可以看到5000端口被​​gunicorn​​​这个进程正在使用,于是将其​​kill​​掉。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_flask_06

杀完之后,再重启容器,首先输入​​docker ps -a​​,查看刚刚创建的容器ID

【目标检测】Flask+Docker在服务器部署YOLOv5应用_目标检测_07


可以看到它的ID是34960ff95951,然后将其启动

docker start 34960ff95951

启动之后,看到终端输入下列内容,表示程序正常运行。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_docker_08


这时候访问服务器公网IP:5000端口,可以看到前端界面已经显示出来。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_flask_09

错误排除

然而,当我上传图片,点击按钮时,突然报错:

RuntimeError: “slow_conv2d_cpu” not implemented for ‘Half’

我在Github的issue[5]中找到了这个问题的答案,原回答如下:

Q: Error “slow_conv2d_cpu” not implemented for ‘Half’
A: In order to save GPU memory consumption and speed up inference, Real-ESRGAN uses half precision (fp16) during inference by default. However, some operators for half inference are not implemented in CPU mode. You need to add --fp32 option for the commands. For example, python inference_realesrgan.py -n RealESRGAN_x4plus.pth -i inputs --fp32

翻译一下,就是本机上为了加速推理,使用了​​model.half()​​半精度(fp16)进行转换,然后,这只能在GPU版本的Pytorch中使用,在CPU版本的Pytorch中会报错。

于是,就得想办法把docker中的文件进行修改,将​​half​​的操作进行移除。

还记得之前DockerFile中指定的路径吗?在之前,指定了Docker工作路径在app文件夹下,因此,可以使用下面的命令,将其拷贝出来。

docker cp 34960ff95951:/app/webapp.py /home/torch/

【目标检测】Flask+Docker在服务器部署YOLOv5应用_python_10


如下图所示,在两个​​half()​​​的地方进行修改,图片直接使用​​float()​​类型。

【目标检测】Flask+Docker在服务器部署YOLOv5应用_flask_11


修改之后,再把文件拷贝回去,这样会覆盖原文件,达到修改的目的。

docker cp  /home/torch/webapp.py 34960ff95951:/app/

修改好之后,重启容器:

docker restart 34960ff95951

然而在此运行时,我又遇到了下面这个报错

AttributeError: ‘Upsample’ object has no attribute ‘recompute_scale_factor’

【目标检测】Flask+Docker在服务器部署YOLOv5应用_目标检测_12


查阅相关资料,这个时pytorch版本的一个Bug,​​upsampling.py​​​文件中,存在了​​recompute_scale_factor​​参数冗余。

和上面操作类似,把该文件拷贝出来,进行修改,再拷贝回去即可。注意该文件属于依赖文件,拷贝出来需要先修改文件的读写权限。

运行效果

排除完这两个错误之后,再次重启容器,上传图片,可以看到推理结果已经正确得呈现出来!

【目标检测】Flask+Docker在服务器部署YOLOv5应用_python_13

总结

本次利用Docker部署遇到许多阻碍。下次部署时,如果服务器是CPU环境,最好先在本地利用CPU运行一下,如果跑通再进行镜像打包。

References

[1]https://github.com/robmarkcole/yolov5-flask
[3]https://www.zsythink.net/archives/4302
[5]https://github.com/xinntao/Real-ESRGAN/blob/master/docs/FAQ.md


以上是关于目标检测Flask+Docker在服务器部署YOLOv5应用的主要内容,如果未能解决你的问题,请参考以下文章

高并发Flask服务部署

docker 部署 flask配置环境及测试

flask+uwsgi+nginx+docker-compose部署

制作JavaCV应用依赖的基础Docker镜像(CentOS7+JDK8+OpenCV4)

制作JavaCV应用依赖的基础Docker镜像(CentOS7+JDK8+OpenCV4)

docker 部署 flask编写及生成镜像,困了,睡午觉,睡醒继续。